usbMIDI sysex Callback function

Status
Not open for further replies.

Gerrit

Well-known member
Looking for a way to receive large amounts of midi sysex data with a Teensy 3.2 I came across this thread: https://forum.pjrc.com/threads/26120-Teensy-USB-MIDI-receive-SysEx-messages where a sysex handler is mentioned. As I'm working on OS X I needed to look in the arduino package to find the usb_midi.h and .c files (in Contents/Java/hardware/teensy/avr/cores/teensy3). I increased the max length in usb_midi.h to 16000
Code:
#define USB_MIDI_SYSEX_MAX 16000  // maximum sysex length we can receive

In the same file I found:
Code:
        inline void setHandleSysEx(void (*fptr)(const uint8_t *data, uint16_t length, bool complete)) {
                usb_midi_handleSysEx = (void (*)(const uint8_t *, uint16_t, uint8_t))fptr;
        }

But when I'm trying to declare a handler like this:

Code:
 usbMIDI.setHandleSysEx(handleSysEx);

with the handling function like this:

Code:
void handleSysEx(byte* sysExData,unsigned int sysExSize,bool complete)
{
 
   Serial.print("The sysex length is ");
   Serial.print(sysExSize);
   Serial.print(" bytes\t");
   Serial.println(" "); 
   
}

I get the following error:

Code:
Zeus-PC3.ino:17:3: error: 'usbMIDI' does not name a type
   usbMIDI.setHandleSysEx(handleSysEx);
   ^
Error compiling for board Teensy 3.2 / 3.1.

What am I doing wrong or missing here?

The sysex length is passed as a uint_16 in the handler so I figure it should be able to handle messages larger than 256 bytes. The read function relies on usbMIDI.getData1 for determining the array length and is unsuitable for large messages as the type is uint_8.


Kind regards,

Gerrit
 
usbMIDI.setHandleSysEx() isn't listed on Paul's page https://www.pjrc.com/teensy/td_midi.html

I've assumed you have to use getSystemArray after testing getType=7

I know that. The problem is that getSysExArray is used together with getData1 to determine the size of the array and that will never work with messages larger than 255 bytes. At least part of the required code seems to be there as far as I can judge. What would it take to implement the sysEx event handler?


Kind regards,

Gerrit
 
If you figure out how to receive large Sysex messages (I think it involves getSysExArray but I've never used it), there's another mod you need to make other than increasing the buffer size to 16000. The code uses a uint8_t as the buffer index and is therefore limited to a message length of at most 256 bytes - this is also true in the standard MIDI library.
In usb_midi.h and usb_midi.c find the declaration of usb_midi_msg_sysex_len and change uint8_t to uint16_t.

Pete
 
P.S. Just read your #3. I'm not sure that getData1 will help you. It seems to return the wrong index. I think what you need is a function which returns usb_midi_msg_sysex_len.
Try adding a function like this:
Code:
        inline uint16_t getsysex_len(void) {
                return usb_midi_msg_sysex_len;
        };

Pete
 
Last edited:
Did you change the Tools|USB Type to MIDI or Serial+MIDI before compiling?

Pete

Doesn't matter, the error occurs in both cases.

Here's the usb_midi.h:

Code:
/* Teensyduino Core Library
 * http://www.pjrc.com/teensy/
 * Copyright (c) 2013 PJRC.COM, LLC.
 *
 * Permission is hereby granted, free of charge, to any person obtaining
 * a copy of this software and associated documentation files (the
 * "Software"), to deal in the Software without restriction, including
 * without limitation the rights to use, copy, modify, merge, publish,
 * distribute, sublicense, and/or sell copies of the Software, and to
 * permit persons to whom the Software is furnished to do so, subject to
 * the following conditions:
 *
 * 1. The above copyright notice and this permission notice shall be
 * included in all copies or substantial portions of the Software.
 *
 * 2. If the Software is incorporated into a build system that allows
 * selection among a list of target devices, then similar target
 * devices manufactured by PJRC.COM must be included in the list of
 * target devices and selectable in the same manner.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
 * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
 * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 */

#ifndef USBmidi_h_
#define USBmidi_h_

#include "usb_desc.h"

#if defined(MIDI_INTERFACE)

#include <inttypes.h>

/*
These were originally meant to allow programs written for
Francois Best's MIDI library to be easily used with
Teensy's usbMIDI which implements the same API.  However,
the MIDI library definitions have changed, so these names
now conflict.  They've never been documented (the PJRC web
page documents usbMIDI.getType() in numbers) so they are
now commented out so usbMIDI and the MIDI library can be
used together without conflict.
#define NoteOff 0
#define NoteOn 1
#define AfterTouchPoly 2
#define ControlChange 3
#define ProgramChange 4
#define AfterTouchChannel 5
#define PitchBend 6
#define SystemExclusive 7
*/

#define USB_MIDI_SYSEX_MAX 16000  // maximum sysex length we can receive

// C language implementation
#ifdef __cplusplus
extern "C" {
#endif
void usb_midi_write_packed(uint32_t n);
void usb_midi_send_sysex(const uint8_t *data, uint32_t length);
void usb_midi_flush_output(void);
int usb_midi_read(uint32_t channel);
extern uint8_t usb_midi_msg_channel;
extern uint8_t usb_midi_msg_type;
extern uint8_t usb_midi_msg_data1;
extern uint8_t usb_midi_msg_data2;
extern uint8_t usb_midi_msg_sysex[USB_MIDI_SYSEX_MAX];
extern uint16_t usb_midi_msg_sysex_len;
extern void (*usb_midi_handleNoteOff)(uint8_t ch, uint8_t note, uint8_t vel);
extern void (*usb_midi_handleNoteOn)(uint8_t ch, uint8_t note, uint8_t vel);
extern void (*usb_midi_handleVelocityChange)(uint8_t ch, uint8_t note, uint8_t vel);
extern void (*usb_midi_handleControlChange)(uint8_t ch, uint8_t control, uint8_t value);
extern void (*usb_midi_handleProgramChange)(uint8_t ch, uint8_t program);
extern void (*usb_midi_handleAfterTouch)(uint8_t ch, uint8_t pressure);
extern void (*usb_midi_handlePitchChange)(uint8_t ch, int pitch);
extern void (*usb_midi_handleSysEx)(const uint8_t *data, uint16_t length, uint8_t complete);
extern void (*usb_midi_handleRealTimeSystem)(uint8_t rtb);
extern void (*usb_midi_handleTimeCodeQuarterFrame)(uint16_t data);

#ifdef __cplusplus
}
#endif

// C++ interface
#ifdef __cplusplus
class usb_midi_class
{
        public:
        void begin(void) { }
        void end(void) { }
        void sendNoteOff(uint32_t note, uint32_t velocity, uint32_t channel) __attribute__((always_inline)) {
		usb_midi_write_packed(0x8008 | (((channel - 1) & 0x0F) << 8)
		  | ((note & 0x7F) << 16) | ((velocity & 0x7F) << 24));
	}
        void sendNoteOn(uint32_t note, uint32_t velocity, uint32_t channel) __attribute__((always_inline)) {
		usb_midi_write_packed(0x9009 | (((channel - 1) & 0x0F) << 8)
		  | ((note & 0x7F) << 16) | ((velocity & 0x7F) << 24));
	}
        void sendPolyPressure(uint32_t note, uint32_t pressure, uint32_t channel) __attribute__((always_inline)) {
		usb_midi_write_packed(0xA00A | (((channel - 1) & 0x0F) << 8)
		  | ((note & 0x7F) << 16) | ((pressure & 0x7F) << 24));
	}
        void sendControlChange(uint32_t control, uint32_t value, uint32_t channel) __attribute__((always_inline)) {
		usb_midi_write_packed(0xB00B | (((channel - 1) & 0x0F) << 8)
		  | ((control & 0x7F) << 16) | ((value & 0x7F) << 24));
	}
        void sendProgramChange(uint32_t program, uint32_t channel) __attribute__((always_inline)) {
		usb_midi_write_packed(0xC00C | (((channel - 1) & 0x0F) << 8)
		  | ((program & 0x7F) << 16));
	}
        void sendAfterTouch(uint32_t pressure, uint32_t channel) __attribute__((always_inline)) {
		usb_midi_write_packed(0xD00D | (((channel - 1) & 0x0F) << 8)
		  | ((pressure & 0x7F) << 16));
	}
        void sendPitchBend(uint32_t value, uint32_t channel) __attribute__((always_inline)) {
		usb_midi_write_packed(0xE00E | (((channel - 1) & 0x0F) << 8)
		  | ((value & 0x7F) << 16) | ((value & 0x3F80) << 17));
	}
        void sendSysEx(uint32_t length, const uint8_t *data) {
		usb_midi_send_sysex(data, length);
	}
	void sendRealTime(uint32_t type) __attribute__((always_inline)) {
		switch (type) {
			case 0xF8: // Clock
			case 0xFA: // Start
			case 0xFC: // Stop
			case 0xFB: // Continue
			case 0xFE: // ActiveSensing
			case 0xFF: // SystemReset
				usb_midi_write_packed((type << 8) | 0x0F);
				break;
			default: // Invalid Real Time marker
				break;
		}
	}
	void sendTimeCodeQuarterFrame(uint32_t type, uint32_t value) __attribute__((always_inline)) {
		uint32_t data = ( ((type & 0x07) << 4) | (value & 0x0F) );
		sendTimeCodeQuarterFrame(data);	
	}
        void sendTimeCodeQuarterFrame(uint32_t data) __attribute__((always_inline)) {
		usb_midi_write_packed(0xF108 | ((data & 0x7F) << 16));
	}
        void send_now(void) {
		usb_midi_flush_output();
	}
        uint8_t analog2velocity(uint16_t val, uint8_t range);
        bool read(uint8_t channel=0) {
		return usb_midi_read(channel);
	}
        inline uint8_t getType(void) {
                return usb_midi_msg_type;
        };
        inline uint8_t getChannel(void) {
                return usb_midi_msg_channel;
        };
        inline uint8_t getData1(void) {
                return usb_midi_msg_data1;
        };
        inline uint8_t getData2(void) {
                return usb_midi_msg_data2;
        };
        inline uint8_t * getSysExArray(void) {
                return usb_midi_msg_sysex;
        };
        inline void setHandleNoteOff(void (*fptr)(uint8_t channel, uint8_t note, uint8_t velocity)) {
                usb_midi_handleNoteOff = fptr;
        };
        inline void setHandleNoteOn(void (*fptr)(uint8_t channel, uint8_t note, uint8_t velocity)) {
                usb_midi_handleNoteOn = fptr;
        };
        inline void setHandleVelocityChange(void (*fptr)(uint8_t channel, uint8_t note, uint8_t velocity)) {
                usb_midi_handleVelocityChange = fptr;
        };
        inline void setHandleControlChange(void (*fptr)(uint8_t channel, uint8_t control, uint8_t value)) {
                usb_midi_handleControlChange = fptr;
        };
        inline void setHandleProgramChange(void (*fptr)(uint8_t channel, uint8_t program)) {
                usb_midi_handleProgramChange = fptr;
        };
        inline void setHandleAfterTouch(void (*fptr)(uint8_t channel, uint8_t pressure)) {
                usb_midi_handleAfterTouch = fptr;
        };
        inline void setHandlePitchChange(void (*fptr)(uint8_t channel, int pitch)) {
                usb_midi_handlePitchChange = fptr;
        };
        inline void setHandleSysEx(void (*fptr)(const uint8_t *data, uint16_t length, bool complete)) {
                usb_midi_handleSysEx = (void (*)(const uint8_t *, uint16_t, uint8_t))fptr;
        }
        inline void setHandleRealTimeSystem(void (*fptr)(uint8_t realtimebyte)) {
                usb_midi_handleRealTimeSystem = fptr;
        };
        inline void setHandleTimeCodeQuarterFrame(void (*fptr)(uint16_t data)) {
                usb_midi_handleTimeCodeQuarterFrame = fptr;
        };
	private:
};

extern usb_midi_class usbMIDI;


#endif // __cplusplus

#endif // MIDI_INTERFACE

#endif // USBmidi_h_

It would be great if the callback could be made to work.

Kind regards,

Gerrit
 
Post your code.

Pete

Here it is:

Code:
/* 
  Zeus Commander Controller Series
  
  
  
  This code is in the public domain.
*/
#include <Bounce.h>
#include <Encoder.h>
#include "GOLDELOX.h"

GOLDELOX oledDisplay(&Serial2);


// USBMidi

usbMIDI.setHandleSysEx(handleSysEx);

//global variables
#define mwIButtonPin 4
#define mwXTButtonPin 5
#define esqMButtonPin 6
#define genericModeButtonPin 7
int bounceTime=5;
Bounce modeButton[4]={Bounce(mwIButtonPin,bounceTime),
                      Bounce(mwXTButtonPin,bounceTime),
                      Bounce(esqMButtonPin,bounceTime),
                      Bounce(genericModeButtonPin,bounceTime),};
                      
elapsedMicros sinceButtonCheck;  // timer for button check
unsigned int buttonCheckInterval=2000;    // interval in microseconds for checking buttons
enum buttonNames{mwI,mwXT,esqM,genericMode};
enum buttonIcons{allUp,mwIdownProgram,mwIdownMulti,mwXTdownProgram,mwXTdownMulti,esqMdown,genericModedown};
uint32_t buttonIconAddress[7]={0x00003c00,0x00005E00,0x00008000,0x0000A200,0x0000C400,0x0000E600,0x00003c00};

// program modes
enum programModes{mwIsound,mwImulti,mwXTsound,mwXTmulti,esqMsound,universal};
uint32_t deviceIconAddress[6]={0x00013C00,0x00013C00,0x00017000,0x00017000,0x0001A400,0x00010800};

int currentProgram[6]={0,0,0,0,0,0};
const int maxProgramNumber[6]={127,127,127,127,119,127}; // maximum program number, used for constraint.
int buttonIcon=allUp;
int programMode=universal;
int mwImode=mwIsound;
int mwXTmode=mwXTsound;

const byte mwIversionNumberRequest[7] = {0xF0,0x3E,0x00,0x00,0x00,0x00,0xF7};
const byte mwIprogramDumpRequest[7] = {0xF0,0x3E,0x00,0x00,0x02,0x00,0xF7};
const byte mwIprogramBankDumpRequest[7] = {0xF0,0x3E,0x00,0x00,0x10,0x00,0xF7};

Encoder encoder(14,15);
long currentEncoderValue=0;
int parameterValue=0;

elapsedMillis sinceDisplayUpdate; // timer for oledDisplay update
unsigned int displayUpdateInterval=50; //
boolean updateDisplay=false;


elapsedMillis sysexRequestTime; // timer for syex request timeout
unsigned int sysexTimeout=5000; // timeout for sysex requests


const char* programNumbersMWXT[256] = {"A  1 \n","A  2 \n","A  3 \n","A  4 \n","A  5 \n","A  6 \n","A  7 \n","A  8 \n","A  9 \n","A 10 \n","A 11 \n","A 12 \n","A 13 \n","A 14 \n","A 15 \n","A 16 \n",
                                       "A 17 \n","A 18 \n","A 19 \n","A 20 \n","A 21 \n","A 22 \n","A 23 \n","A 24 \n","A 25 \n","A 26 \n","A 27 \n","A 28 \n","A 29 \n","A 30 \n","A 31 \n","A 32 \n",
                                       "A 33 \n","A 34 \n","A 35 \n","A 36 \n","A 37 \n","A 38 \n","A 39 \n","A 40 \n","A 41 \n","A 42 \n","A 43 \n","A 44 \n","A 45 \n","A 46 \n","A 47 \n","A 48 \n",
                                       "A 49 \n","A 50 \n","A 51 \n","A 52 \n","A 53 \n","A 54 \n","A 55 \n","A 56 \n","A 57 \n","A 58 \n","A 59 \n","A 60 \n","A 61 \n","A 62 \n","A 63 \n","A 64 \n",
                                       "A 65 \n","A 66 \n","A 67 \n","A 68 \n","A 69 \n","A 70 \n","A 71 \n","A 72 \n","A 73 \n","A 74 \n","A 75 \n","A 76 \n","A 77 \n","A 78 \n","A 79 \n","A 80 \n",
                                       "A 81 \n","A 82 \n","A 83 \n","A 84 \n","A 85 \n","A 86 \n","A 87 \n","A 88 \n","A 89 \n","A 90 \n","A 91 \n","A 92 \n","A 93 \n","A 94 \n","A 95 \n","A 96 \n",
                                       "A 97 \n","A 98 \n","A 99 \n","A100 \n","A101 \n","A102 \n","A103 \n","A104 \n","A105 \n","A106 \n","A107 \n","A108 \n","A109 \n","A110 \n","A111 \n","A112 \n",
                                       "A113 \n","A114 \n","A115 \n","A116 \n","A117 \n","A118 \n","A119 \n","A120 \n","A121 \n","A122 \n","A123 \n","A124 \n","A125 \n","A126 \n","A127 \n","A128 \n",
                                       "B  1 \n","B  2 \n","B  3 \n","B  4 \n","B  5 \n","B  6 \n","B  7 \n","B  8 \n","B  9 \n","B 10 \n","B 11 \n","B 12 \n","B 13 \n","B 14 \n","B 15 \n","B 16 \n",
                                       "B 17 \n","B 18 \n","B 19 \n","B 20 \n","B 21 \n","B 22 \n","B 23 \n","B 24 \n","B 25 \n","B 26 \n","B 27 \n","B 28 \n","B 29 \n","B 30 \n","B 31 \n","B 32 \n",
                                       "B 33 \n","B 34 \n","B 35 \n","B 36 \n","B 37 \n","B 38 \n","B 39 \n","B 40 \n","B 41 \n","B 42 \n","B 43 \n","B 44 \n","B 45 \n","B 46 \n","B 47 \n","B 48 \n",
                                       "B 49 \n","B 50 \n","B 51 \n","B 52 \n","B 53 \n","B 54 \n","B 55 \n","B 56 \n","B 57 \n","B 58 \n","B 59 \n","B 60 \n","B 61 \n","B 62 \n","B 63 \n","B 64 \n",
                                       "B 65 \n","B 66 \n","B 67 \n","B 68 \n","B 69 \n","B 70 \n","B 71 \n","B 72 \n","B 73 \n","B 74 \n","B 75 \n","B 76 \n","B 77 \n","B 78 \n","B 79 \n","B 80 \n",
                                       "B 81 \n","B 82 \n","B 83 \n","B 84 \n","B 85 \n","B 86 \n","B 87 \n","B 88 \n","B 89 \n","B 90 \n","B 91 \n","B 92 \n","B 93 \n","B 94 \n","B 95 \n","B 96 \n",
                                       "B 97 \n","B 98 \n","B 99 \n","B100 \n","B101 \n","B102 \n","B103 \n","B104 \n","B105 \n","B106 \n","B107 \n","B108 \n","B109 \n","B110 \n","B111 \n","B112 \n",
                                       "B113 \n","B114 \n","B115 \n","B116 \n","B117 \n","B118 \n","B119 \n","B120 \n","B121 \n","B122 \n","B123 \n","B124 \n","B125 \n","B126 \n","B127 \n","B128 \n"};
const char* programNumbersMWI[128] = {"A  1 \n","A  2 \n","A  3 \n","A  4 \n","A  5 \n","A  6 \n","A  7 \n","A  8 \n",
                                      "A  9 \n","A 10 \n","A 11 \n","A 12 \n","A 13 \n","A 14 \n","A 15 \n","A 16 \n",
                                      "A 17 \n","A 18 \n","A 19 \n","A 20 \n","A 21 \n","A 22 \n","A 23 \n","A 24 \n",
                                      "A 25 \n","A 26 \n","A 27 \n","A 28 \n","A 29 \n","A 30 \n","A 31 \n","A 32 \n",
                                      "B  1 \n","B  2 \n","B  3 \n","B  4 \n","B  5 \n","B  6 \n","B  7 \n","B  8 \n",
                                      "B  9 \n","B 10 \n","B 11 \n","B 12 \n","B 13 \n","B 14 \n","B 15 \n","B 16 \n",
                                      "B 17 \n","B 18 \n","B 19 \n","B 20 \n","B 21 \n","B 22 \n","B 23 \n","B 24 \n",
                                      "B 25 \n","B 26 \n","B 27 \n","B 28 \n","B 29 \n","B 30 \n","B 31 \n","B 32 \n",
                                      "C  1 \n","C  2 \n","C  3 \n","C  4 \n","C  5 \n","C  6 \n","C  7 \n","C  8 \n",
                                      "C  9 \n","C 10 \n","C 11 \n","C 12 \n","C 13 \n","C 14 \n","C 15 \n","C 16 \n",
                                      "C 17 \n","C 18 \n","C 19 \n","C 20 \n","C 21 \n","C 22 \n","C 23 \n","C 24 \n",
                                      "C 25 \n","C 26 \n","C 27 \n","C 28 \n","C 29 \n","C 30 \n","C 31 \n","C 32 \n",
                                      "D  1 \n","D  2 \n","D  3 \n","D  4 \n","D  5 \n","D  6 \n","D  7 \n","D  8 \n",
                                      "D  9 \n","D 10 \n","D 11 \n","D 12 \n","D 13 \n","D 14 \n","D 15 \n","D 16 \n",
                                      "D 17 \n","D 18 \n","D 19 \n","D 20 \n","D 21 \n","D 22 \n","D 23 \n","D 24 \n",
                                      "D 25 \n","D 26 \n","D 27 \n","D 28 \n","D 29 \n","D 30 \n","D 31 \n","D 32 \n",};


// This routine will try to mount and initialize the uSD card. If successful,
// it returns a TRUE, otherwise FALSE.


int led = 13;
boolean fmediatests;

// handle incoming sysex messages
void handleSysEx(byte* sysExData,unsigned int sysExSize,bool complete)
{
 
   Serial.print("The sysex length is ");
   Serial.print(sysExSize);
   Serial.print(" bytes\t");
   Serial.println(" "); 
   
}


void setup() {
  uint16_t result;
  // put your setup code here, to run once:
    delay (2000); //let the display start up  
    Serial2.begin(115200);
 // define pin modes for  led pins:
    pinMode(mwIButtonPin,INPUT_PULLUP);
    pinMode(mwXTButtonPin,INPUT_PULLUP);
    pinMode(esqMButtonPin,INPUT_PULLUP);
    pinMode(genericModeButtonPin,INPUT_PULLUP);
  
  //--------------------------------Optional reset routine-----------------------------------
  //Reset the Display using D4 of the Arduino (if using the new 4D Arduino Adaptor - Rev 2)
  //If using the old 4D Arduino Adaptor (Rev 1), change D4 to D2 below.
  //If using jumper wires, reverse the logic states below. 
  //Refer to the accompanying application note for important information.
  pinMode(2, OUTPUT);  // Set D4 on Arduino to Output (4D Arduino Adaptor V2 - Display Reset)
  digitalWrite(2,0);  // Reset the Display via D2
  delay(100);
  digitalWrite(2,1);  // unReset the Display via D4
  //-----------------------------------------END---------------------------------------------
  

  
    delay (4000); //let the display start up  

  
  oledDisplay.putString((char *)"                         \n");
  oledDisplay.mediaInit(&result) ;
    delay(100);
    // CLEAR 40 pixels high
  oledDisplay.setByteAddress(0x00010800);
  oledDisplay.displayImageRaw(0, 0) ;

      delay(100);
  oledDisplay.setSectorAddress(0x0000);
  oledDisplay.setFont(7);
  oledDisplay.moveCursor(0,0);
  oledDisplay.putString((char *)"Sound\n");
  
  oledDisplay.moveCursor(8,0);
  displayProgramNumber(currentProgram[programMode]);
  // test buttons
  // ALL UP
  
      delay(100);
  oledDisplay.setByteAddress(0x00003c00);
  oledDisplay.displayImageRaw(0, 94) ;
}

void loop() {
    //usbMIDI
  usbMIDI.read(); 
  // check elapsedMicros and millis timers
  if (sinceButtonCheck >=buttonCheckInterval) {
    sinceButtonCheck = sinceButtonCheck - buttonCheckInterval;
    handleButtons();
    handleEncoder();
  }  
  if (sinceDisplayUpdate >=displayUpdateInterval) {
    sinceDisplayUpdate = sinceDisplayUpdate - displayUpdateInterval;
    handleDisplay();
  }
}

// handle soft buttons
void handleButtons(){
  // array for current button state
  int buttonState[4];
  // update buttons and variable            
  for (int i=0;i<4;i++){
    modeButton[i].update();
    buttonState[i]=modeButton[i].read();
  }
  // buttons: mwI, mwXT, esqM, genericMode
  // program modes: mwIsound, mwImulti, mwXTsound, mwXTmulti, esqM, universal
  // buttonIcons: allUp,mwIdownProgram, mwIdownMulti, mwXTdownProgram, mwXTdownMulti, esqMdown, genericModedown
  // microwave I button pressed
  if (buttonState[mwI]==LOW && buttonState[mwXT]==HIGH && buttonState[esqM]==HIGH && buttonState[genericMode]==HIGH){
    if (mwImode==mwIsound){
      if(buttonIcon!=mwIdownProgram){
        //button mwIdown multi choice
        setButtonState(mwIdownProgram);
      }
    } else if (mwImode==mwImulti){
      if(buttonIcon!=mwIdownMulti){
        //button mwIdown program choice
        setButtonState(mwIdownMulti);
      }
    }
    if (programMode==mwXTsound || programMode==mwXTmulti || programMode==esqMsound || programMode==universal){
      switchProgramMode(mwImode);
    }
    if( modeButton[esqM].risingEdge()){
      if (mwImode==mwIsound){
        mwImode=mwImulti;
        programMode=mwImulti;
        oledDisplay.moveCursor(0,0);
        oledDisplay.putString((char *)"Multi  \n");
        updateDisplay=true;
      } else if (mwImode==mwImulti){
        mwImode=mwIsound;
        programMode=mwIsound;
        oledDisplay.moveCursor(0,0);
        oledDisplay.putString((char *)"Sound\n");
        updateDisplay=true;
      }
    }
    if( modeButton[genericMode].risingEdge()){
      usbMIDI.sendSysEx(7,mwIversionNumberRequest);
    }
  }
  // microwave XT button pressed
  if (buttonState[mwI]==HIGH && buttonState[mwXT]==LOW && buttonState[esqM]==HIGH && buttonState[genericMode]==HIGH){
    //show mwXT button pressed
    if(mwXTmode==mwXTsound){
      if(buttonIcon!=mwXTdownProgram){
        //button mwXTdown multi choice
        setButtonState(mwXTdownProgram);
      }
    }else if (mwXTmode==mwXTmulti){
      if(buttonIcon!=mwXTdownMulti){
        //button mwXTdown multi choice
        setButtonState(mwXTdownMulti);
      }
    }
    if (programMode==mwIsound || programMode == mwImulti || programMode==esqMsound || programMode==universal){
      switchProgramMode(mwXTmode);
    }
    if( modeButton[esqM].risingEdge()){
     if (mwXTmode==mwXTsound){
        mwXTmode=mwXTmulti;
        programMode=mwXTmulti;
        oledDisplay.moveCursor(0,0);
        oledDisplay.putString((char *)"Multi  \n");
        //  updateDisplay=true;
      } else if (mwXTmode==mwXTmulti){
        mwXTmode=mwXTsound;
        programMode=mwXTsound;
        oledDisplay.moveCursor(0,0);
        oledDisplay.putString((char *)"Sound\n");
        //  updateDisplay=true;
      }
    }
  }
  // esqM button pressed
  if (buttonState[mwI]==HIGH && buttonState[mwXT]==HIGH && buttonState[esqM]==LOW && buttonState[genericMode]==HIGH){
    //show esqM button pressed
    if (buttonIcon!=esqMdown){
        setButtonState(esqMdown);
    }
    if (programMode!=esqMsound){
      switchProgramMode(esqMsound);
    }
  }
  // generic mode button pressed
  if (buttonState[mwI]==HIGH && buttonState[mwXT]==HIGH && buttonState[esqM]==HIGH && buttonState[genericMode]==LOW){
    //show all buttons up
    if (buttonIcon!=allUp){
        setButtonState(allUp);
    }
    if (programMode!=universal){
      switchProgramMode(universal);
    }
  }
  // all buttons up
  if (buttonState[mwI]==HIGH && buttonState[mwXT]==HIGH && buttonState[esqM]==HIGH && buttonState[genericMode]==HIGH){
    //show all buttons up
    if (buttonIcon!=allUp){
        setButtonState(allUp);
    }
  }
  
}

// set button state and update button icon
void setButtonState(int state){
  buttonIcon=state;
  oledDisplay.setByteAddress(buttonIconAddress[buttonIcon]);
  oledDisplay.displayImageRaw(0, 94) ;
}

// handle rotary encoder
void handleEncoder(){
    // long for new encoder value
  long newEncoderValue = 0;
        newEncoderValue=encoder.read();
      if (abs(newEncoderValue-currentEncoderValue)>3) 
        {
          currentProgram[programMode]=constrain(currentProgram[programMode]+int((newEncoderValue-currentEncoderValue)/4),0,maxProgramNumber[programMode]);
          currentEncoderValue=newEncoderValue;
          usbMIDI.sendProgramChange(currentProgram[programMode],1);
          updateDisplay=true;
        }
}

// update number & name for selected device
void handleDisplay(){
  if (updateDisplay){
    oledDisplay.moveCursor(7,0);
    switch (programMode){
      case mwIsound:
        oledDisplay.putString((char *)programNumbersMWI[currentProgram[programMode]]);
        //usbMIDI.sendSysEx(7,mwIprogramDumpRequest);
        break;
      case mwImulti:
        oledDisplay.putString((char *)programNumbersMWI[currentProgram[programMode]]);
        break;
      case mwXTsound:
        oledDisplay.putString((char *)programNumbersMWXT[currentProgram[programMode]]);
        break;
      case mwXTmulti:
        oledDisplay.putString((char *)programNumbersMWXT[currentProgram[programMode]]);
        break;
      case esqMsound:
        //oledDisplay.putString((char *)programNumbersESQM[currentProgram[programMode]]);
        displayProgramNumber(currentProgram[programMode]);
        break;
      case universal:
        //oledDisplay.putString((char *)programNumbers[currentProgram[programMode]]);
        displayProgramNumber(currentProgram[programMode]);
        break;
    }
    updateDisplay=false;
  }
}

void displayProgramNumber(int number){
  char stringBuffer[5];
  dtostrf(float(number), 5, 0, stringBuffer);
  oledDisplay.putString((char *)stringBuffer);
} 


// switch device, display appropriate icon
void switchProgramMode(int mode){
  programMode=mode;
  // show appropriate icon
  oledDisplay.setByteAddress(deviceIconAddress[programMode]);
  oledDisplay.displayImageRaw(0, 50) ;
  updateDisplay=true;
}


void requestMidiDump(int device){
    switch (device){
    case mwI:
      // MW I
      
      break;
    case mwXT:
      // MW XT
      break;
    case esqM:
      // ESQ-M
      break;
    default:
    break;
  }
  
}

It is work in progress, I was just working on getting rid of the large arrays to clear up variable space.

Kind regards,

Gerrit
 
The problem is that getSysExArray is used together with getData1 to determine the size of the array ...
Apologies if this is already covered in the rest of your discussion with el_supremo...

I would assume you have to scan for the terminating byte for the sysex ... since you have a pointer anyway can't you just keep incrementing until the byte you read equals 0xF7?



...it's what I meant in this earlier discussion but looking back I recall it was never settled.
https://forum.pjrc.com/threads/38367-Teensy-2-0-USB-MIDI-and-SysEx?p=132023&viewfull=1#post132023

[edit ] a guess post 4 tells me this would not work.. thanks Pete!
 
Last edited:
This should be in the setup function.
Code:
usbMIDI.setHandleSysEx(handleSysEx);

and change your definition of handleSysEx to:
Code:
void handleSysEx(const byte* sysExData, uint16_t sysExSize, bool complete)

Pete
 
I've been looking at how sysex is handled and it appears that you don't really need to extend the size of the sysex array to be able to handle long sysex messages. The "bool complete" argument of your sysex handler function will be false until the library reads the end of the sysex message and when it passes you that last buffer, "complete" will be true.
Of course, if you edit the library and make the buffer longer than the longest sysex you ever receive, "complete" should always be true each time that the handler is called.

Pete
 
This should be in the setup function.
Code:
usbMIDI.setHandleSysEx(handleSysEx);

and change your definition of handleSysEx to:
Code:
void handleSysEx(const byte* sysExData, uint16_t sysExSize, bool complete)

Pete

Thank you very much! Silly mistakes on my part:eek: , this is due the unfamiliarity with the syntax and grammar.

I cleaned up the code in the meantime and got rid of the large arrays:

Code:
/* 
  Zeus Commander Controller Series
  
  
  
  This code is in the public domain.
*/
#include <Bounce.h>
#include <Encoder.h>
#include "GOLDELOX.h"

GOLDELOX oledDisplay(&Serial2);

//global variables
#define mwIButtonPin 4
#define mwXTButtonPin 5
#define esqMButtonPin 6
#define genericModeButtonPin 7
int bounceTime=5;
Bounce modeButton[4]={Bounce(mwIButtonPin,bounceTime),
                      Bounce(mwXTButtonPin,bounceTime),
                      Bounce(esqMButtonPin,bounceTime),
                      Bounce(genericModeButtonPin,bounceTime),};
                      
elapsedMicros   sinceButtonCheck;                         // timer for button check
unsigned int    buttonCheckInterval=2000;                 // interval in microseconds for checking buttons
enum            buttonNames{mwI,mwXT,esqM,genericMode};
enum            buttonStates{allUp,mwIdownProgram,mwIdownMulti,mwXTdownProgram,mwXTdownMulti,esqMdown,genericModedown};
uint32_t        buttonIconAddress[7]={0x00003c00,0x00005E00,0x00008000,0x0000A200,0x0000C400,0x0000E600,0x00003c00};

// program modes, a combination of device and program/multi mode
enum        programModes{mwIsound,mwImulti,mwXTsound,mwXTmulti,esqMsound,universal};                     
uint32_t    deviceIconAddress[6]={0x00013C00,0x00013C00,0x00017000,0x00017000,0x0001A400,0x00010800};     // adresses of images on SD card on display

int         currentProgram[6]={0,0,0,0,0,0};                                                              // current selected program for mode
const int   maxProgramNumber[6]={127,255,255,127,119,127};                                                // maximum program number, used for constraint.
const int   bankSize[6]={32,32,127,127,119,127};                                                          // number of programs in bank.
const char  bank[5]={' ','A','B','C','D'};                                                                // letters for bank display
const int   minProgram[6]={1,1,1,1,1,0};                                                                  // minimum program number
const int   minBankIndex[6]={1,1,1,1,1,0};                                                                // minimum bank index


int buttonIcon=allUp;
int programMode=universal;
int mwImode=mwIsound;
int mwXTmode=mwXTsound;

// MIDI sysEx messages for requesting program and banks
const byte mwIversionNumberRequest[7] = {0xF0,0x3E,0x00,0x00,0x00,0x00,0xF7};
const byte mwIprogramDumpRequest[7] = {0xF0,0x3E,0x00,0x00,0x02,0x00,0xF7};
const byte mwIprogramBankDumpRequest[7] = {0xF0,0x3E,0x00,0x00,0x10,0x00,0xF7};

Encoder encoder(14,15);
long    currentEncoderValue=0;
int     parameterValue=0;

elapsedMillis sinceDisplayUpdate;         // timer for oledDisplay update
unsigned int  displayUpdateInterval=50;   // display update interval in milliseconds
boolean       updateDisplay=false;


elapsedMillis sysexRequestTime; // timer for syex request timeout
unsigned int sysexTimeout=5000; // timeout for sysex requests

// handle incoming sysex messages
void handleSysEx(const byte* sysExData, uint16_t sysExSize, bool complete)
{
  if(complete){
  //simple test to see if the complete message is available
   Serial.print("The sysex length is ");
   Serial.print(sysExSize);
   Serial.print(" bytes\t");
   Serial.println(" "); 
  }
}


void setup() {
  usbMIDI.setHandleSysEx(handleSysEx);
  uint16_t result;
  Serial.begin(115200);
  // put your setup code here, to run once:
    delay (2000); //let the display start up  
    Serial2.begin(115200);
  // define pin modes for button pins:
  pinMode(mwIButtonPin,INPUT_PULLUP);
  pinMode(mwXTButtonPin,INPUT_PULLUP);
  pinMode(esqMButtonPin,INPUT_PULLUP);
  pinMode(genericModeButtonPin,INPUT_PULLUP);
  
  // displayreset
  pinMode(2, OUTPUT); 
  digitalWrite(2,0);  // Reset the Display
  delay(100);
  digitalWrite(2,1);  // unReset display
  delay (4000); //let the display start up  

  oledDisplay.mediaInit(&result) ;
  oledDisplay.setSectorAddress(0x0000);
  oledDisplay.setFont(7);
  oledDisplay.moveCursor(0,0);
  oledDisplay.putString((char *)"Sound\n");
  oledDisplay.moveCursor(7,0);
  displayProgramNumber(currentProgram[programMode]);
  // display ALL BUTTONS UP icon
  oledDisplay.setByteAddress(0x00003c00);
  oledDisplay.displayImageRaw(0, 94) ;
}

void loop() {
    //usbMIDI
  usbMIDI.read(); 
  // check elapsedMicros and millis timers
  if (sinceButtonCheck >=buttonCheckInterval) {
    sinceButtonCheck = sinceButtonCheck - buttonCheckInterval;
    handleButtons();
    handleEncoder();
  }  
  if (sinceDisplayUpdate >=displayUpdateInterval) {
    sinceDisplayUpdate = sinceDisplayUpdate - displayUpdateInterval;
    handleDisplay();
  }
}

// handle soft buttons
void handleButtons(){
  // array for current button state
  int buttonState[4];
  // update buttons and variable            
  for (int i=0;i<4;i++){
    modeButton[i].update();
    buttonState[i]=modeButton[i].read();
  }
  // buttons: mwI, mwXT, esqM, genericMode
  // program modes: mwIsound, mwImulti, mwXTsound, mwXTmulti, esqM, universal
  // buttonIcons: allUp,mwIdownProgram, mwIdownMulti, mwXTdownProgram, mwXTdownMulti, esqMdown, genericModedown
  // microwave I button pressed
  if (buttonState[mwI]==LOW && buttonState[mwXT]==HIGH && buttonState[esqM]==HIGH && buttonState[genericMode]==HIGH){
    if (mwImode==mwIsound){
      if(buttonIcon!=mwIdownProgram){
        //button mwIdown multi choice
        setButtonState(mwIdownProgram);
      }
    } else if (mwImode==mwImulti){
      if(buttonIcon!=mwIdownMulti){
        //button mwIdown program choice
        setButtonState(mwIdownMulti);
      }
    }
    if (programMode==mwXTsound || programMode==mwXTmulti || programMode==esqMsound || programMode==universal){
      switchProgramMode(mwImode);
    }
    if( modeButton[esqM].risingEdge()){
      if (mwImode==mwIsound){
        mwImode=mwImulti;
        programMode=mwImulti;
        oledDisplay.moveCursor(0,0);
        oledDisplay.putString((char *)"Multi  \n");
        updateDisplay=true;
      } else if (mwImode==mwImulti){
        mwImode=mwIsound;
        programMode=mwIsound;
        oledDisplay.moveCursor(0,0);
        oledDisplay.putString((char *)"Sound\n");
        updateDisplay=true;
      }
    }
    if( modeButton[genericMode].risingEdge()){
      usbMIDI.sendSysEx(7,mwIversionNumberRequest);
    }
  }
  // microwave XT button pressed
  if (buttonState[mwI]==HIGH && buttonState[mwXT]==LOW && buttonState[esqM]==HIGH && buttonState[genericMode]==HIGH){
    //show mwXT button pressed
    if(mwXTmode==mwXTsound){
      if(buttonIcon!=mwXTdownProgram){
        //button mwXTdown multi choice
        setButtonState(mwXTdownProgram);
      }
    }else if (mwXTmode==mwXTmulti){
      if(buttonIcon!=mwXTdownMulti){
        //button mwXTdown multi choice
        setButtonState(mwXTdownMulti);
      }
    }
    if (programMode==mwIsound || programMode == mwImulti || programMode==esqMsound || programMode==universal){
      switchProgramMode(mwXTmode);
    }
    if( modeButton[esqM].risingEdge()){
     if (mwXTmode==mwXTsound){
        mwXTmode=mwXTmulti;
        programMode=mwXTmulti;
        oledDisplay.moveCursor(0,0);
        oledDisplay.putString((char *)"Multi  \n");
        //  updateDisplay=true;
      } else if (mwXTmode==mwXTmulti){
        mwXTmode=mwXTsound;
        programMode=mwXTsound;
        oledDisplay.moveCursor(0,0);
        oledDisplay.putString((char *)"Sound\n");
        //  updateDisplay=true;
      }
    }
  }
  // esqM button pressed
  if (buttonState[mwI]==HIGH && buttonState[mwXT]==HIGH && buttonState[esqM]==LOW && buttonState[genericMode]==HIGH){
    //show esqM button pressed
    if (buttonIcon!=esqMdown){
        setButtonState(esqMdown);
    }
    if (programMode!=esqMsound){
      switchProgramMode(esqMsound);
    }
  }
  // generic mode button pressed
  if (buttonState[mwI]==HIGH && buttonState[mwXT]==HIGH && buttonState[esqM]==HIGH && buttonState[genericMode]==LOW){
    //show all buttons up
    if (buttonIcon!=allUp){
        setButtonState(allUp);
    }
    if (programMode!=universal){
      switchProgramMode(universal);
    }
  }
  // all buttons up
  if (buttonState[mwI]==HIGH && buttonState[mwXT]==HIGH && buttonState[esqM]==HIGH && buttonState[genericMode]==HIGH){
    //show all buttons up
    if (buttonIcon!=allUp){
        setButtonState(allUp);
    }
  }
  
}

// set button state and update button icon
void setButtonState(int state){
  buttonIcon=state;
  oledDisplay.setByteAddress(buttonIconAddress[buttonIcon]);
  oledDisplay.displayImageRaw(0, 94) ;
}

// handle rotary encoder
void handleEncoder(){
    // long for new encoder value
  long newEncoderValue = 0;
        newEncoderValue=encoder.read();
      if (abs(newEncoderValue-currentEncoderValue)>3) 
        {
          currentProgram[programMode]=constrain(currentProgram[programMode]+int((newEncoderValue-currentEncoderValue)/4),0,maxProgramNumber[programMode]);
          currentEncoderValue=newEncoderValue;
          usbMIDI.sendProgramChange(currentProgram[programMode],1);
          updateDisplay=true;
        }
}

// update number & name for selected device
void handleDisplay(){
  if (updateDisplay){
    oledDisplay.moveCursor(7,0);
    displayProgramNumber(currentProgram[programMode]);
    updateDisplay=false;
  }
}

// display program number in bank format specified in programMode arrays
void displayProgramNumber(int number){
  int bankIndex=0;
  int program=0;
  char stringBuffer[5];
  program=(number % bankSize[programMode]) + minProgram[programMode];
  bankIndex=floor(number/bankSize[programMode]) + minBankIndex[programMode];
  dtostrf(float(program), 5, 0, stringBuffer);
  stringBuffer[0]=bank[bankIndex];
  oledDisplay.putString((char *)stringBuffer);
} 

// switch device, display appropriate icon
void switchProgramMode(int mode){
  programMode=mode;
  // show appropriate icon
  oledDisplay.setByteAddress(deviceIconAddress[programMode]);
  oledDisplay.displayImageRaw(0, 50) ;
  updateDisplay=true;
}

void requestMidiDump(int device){
    switch (device){
    case mwI:
      // MW I
      
      break;
    case mwXT:
      // MW XT
      break;
    case esqM:
      // ESQ-M
      break;
    default:
    break;
  }
  
}

No more error! Alas, something is still wrong. When I send a 11.4KB file the reported length is 7bytes. Tomorrow I will try a single program request, this is under 255 bytes, and see what happens then.

Kind regards,

Gerrit
 
Have you edited usb_midi.h and usb_midi.cpp and, if so, what have you changed?


Exactly how many bytes are in the file?

Pete

I edited usb_midi.h to increase the size of the sysex and added the declaration of the handler function. Here's the content:

Code:
#ifndef USBserial_h_
#define USBserial_h_

#include <inttypes.h>

#include "Stream.h"

#define USB_MIDI_SYSEX_MAX 16000  // maximum sysex length we can receive

/*
These were originally meant to allow programs written for
Francois Best's MIDI library to be easily used with
Teensy's usbMIDI which implements the same API.  However,
the MIDI library definitions have changed, so these names
now conflict.  They've never been documented (the PJRC web
page documents usbMIDI.getType() in numbers) so they are
now commented out so usbMIDI and the MIDI library can be
used together without conflict.
#define NoteOff 0
#define NoteOn 1
#define AfterTouchPoly 2
#define ControlChange 3
#define ProgramChange 4
#define AfterTouchChannel 5
#define PitchBend 6
#define SystemExclusive 7
*/

class usb_midi_class
{
public:
	void sendNoteOff(uint8_t note, uint8_t velocity, uint8_t channel);
	void sendNoteOn(uint8_t note, uint8_t velocity, uint8_t channel);
	void sendPolyPressure(uint8_t note, uint8_t pressure, uint8_t channel);
	void sendControlChange(uint8_t control, uint8_t value, uint8_t channel);
	void sendProgramChange(uint8_t program, uint8_t channel);
	void sendAfterTouch(uint8_t pressure, uint8_t channel);
	void sendPitchBend(uint16_t value, uint8_t channel);
	void sendSysEx(uint8_t length, const uint8_t *data);
	void send_now(void);
	uint8_t analog2velocity(uint16_t val, uint8_t range);
	bool read(uint8_t channel=0);
	inline uint8_t getType(void) {
		return msg_type;
	};
	inline uint8_t getChannel(void) {
		return msg_channel;
	};
	inline uint8_t getData1(void) {
		return msg_data1;
	};
	inline uint8_t getData2(void) {
		return msg_data2;
	};
	inline uint8_t * getSysExArray(void) {
		return msg_sysex;
	};
	inline void setHandleNoteOff(void (*fptr)(uint8_t channel, uint8_t note, uint8_t velocity)) {
		handleNoteOff = fptr;
	};
	inline void setHandleNoteOn(void (*fptr)(uint8_t channel, uint8_t note, uint8_t velocity)) {
		handleNoteOn = fptr;
	};
	inline void setHandleVelocityChange(void (*fptr)(uint8_t channel, uint8_t note, uint8_t velocity)) {
		handleVelocityChange = fptr;
	};
	inline void setHandleControlChange(void (*fptr)(uint8_t channel, uint8_t control, uint8_t value)) {
		handleControlChange = fptr;
	};
	inline void setHandleProgramChange(void (*fptr)(uint8_t channel, uint8_t program)) {
		handleProgramChange = fptr;
	};
	inline void setHandleAfterTouch(void (*fptr)(uint8_t channel, uint8_t pressure)) {
		handleAfterTouch = fptr;
	};
	inline void setHandlePitchChange(void (*fptr)(uint8_t channel, int pitch)) {
		handlePitchChange = fptr;
	};
  inline void setHandleSysEx(void (*fptr)(const uint8_t *data, uint16_t length, bool complete)) {
          usb_midi_handleSysEx = (void (*)(const uint8_t *, uint16_t, uint8_t))fptr;
  }
	inline void setHandleRealTimeSystem(void (*fptr)(uint8_t realtimebyte)) {
		handleRealTimeSystem = fptr;
	};
private:
	void send_raw(uint8_t b0, uint8_t b1, uint8_t b2, uint8_t b3);
	void read_sysex_byte(uint8_t b);
	uint8_t msg_channel;
	uint8_t msg_type;
	uint8_t msg_data1;
	uint8_t msg_data2;
	uint8_t msg_sysex[USB_MIDI_SYSEX_MAX];
	uint16_t msg_sysex_len;
	void (*handleNoteOff)(uint8_t ch, uint8_t note, uint8_t vel);
	void (*handleNoteOn)(uint8_t ch, uint8_t note, uint8_t vel);
	void (*handleVelocityChange)(uint8_t ch, uint8_t note, uint8_t vel);
	void (*handleControlChange)(uint8_t ch, uint8_t, uint8_t);
	void (*handleProgramChange)(uint8_t ch, uint8_t);
	void (*handleAfterTouch)(uint8_t ch, uint8_t);
	void (*handlePitchChange)(uint8_t ch, int pitch);
	void (*handleRealTimeSystem)(uint8_t rtb);
};

extern usb_midi_class usbMIDI;


class usb_serial_class : public Stream
{
public:
	// standard Arduino functions
	void begin(long);
	void end();
	virtual int available();
	virtual int read();
	virtual int peek();
	virtual void flush();
#if ARDUINO >= 100
	virtual size_t write(uint8_t);
#else
	virtual void write(uint8_t);
#endif
	using Print::write;
	operator bool();
	// Teensy extensions
	void send_now(void);
	uint32_t baud(void);
	uint8_t stopbits(void);
	uint8_t paritytype(void);
	uint8_t numbits(void);
	uint8_t dtr(void);
	uint8_t rts(void);
private:
	uint8_t readnext(void);
};

extern usb_serial_class Serial;


#endif

Perhaps it is a good idea to do a fresh teensyduino install and take it from there?

I changed the handler to display the content of the message:

Code:
void handleSysEx(const byte* sysExData, uint16_t sysExSize, bool complete)
{
  if(complete){
  //simple test to see if the complete message is available
   Serial.print("The sysex length is ");
   Serial.print(sysExSize);
   Serial.print(" bytes\t");
   Serial.println(); 
   for (int i=0;i<=sysExSize;i++){
    
   Serial.print(sysExData[i],HEX);
   Serial.print(" ");
   }
   Serial.println(); 
  }
}

The exact size of the message is 11527 bytes. I use the 'SysEx Librarian' application for OS X to send the file and the 'MIDI Monitor' application to view the messages being sent. The output on the arduino serial monitor is:

Code:
The sysex length is 7 bytes	
F0 3E 0 0 50 20 0 40

These are the first bytes in the file:

Code:
0000  F0 3E 00 00 50 20 00 40  02 00 00 0C 54 05 40 00  | >  P  @    T @ |
0010  20 00 4A 02 00 00 0C 54  05 40 00 00 00 3F 00 40  |  J    T @   ? @|
0020  40 40 00 0C 40 03 40 01  00 3F 00 40 40 40 00 0C  |@@  @ @  ? @@@  |
...

I'll try some smaller messages now to see if I can successfully process them. I already have the code for emulating a Logic (Mackie) Control hardware DAW controller running on an Arduino Due using the serial MIDI library. The sysEx messages used in this context are all less than 255 bytes so it should be possible to port the code to the teensy usbMIDI library. If and when I have this working I'd be more than happy to write some documentation and create an example of the use of sysEx callback.

Kind regards,

Gerrit
 
To exclude errors caused by fiddling with the code by me I installed a fresh teensyduino. The only change I made was increasing USB_MIDI_SYSEX_MAX in usb_midi.h only.

Code:
#define USB_MIDI_SYSEX_MAX 255  // maximum sysex length we can receive

If I now send a single program (187 bytes) to the teensy using the SysEx librarian the handler works perfectly and consistently:

Code:
The sysex length is 187 bytes	
F0 3E 0 0 42 10 0 40 4 0 0 1 40 5 41 2 10 0 41 4 0 3 7 4C 1 40 0 0 D 0 20 48 40 60 0 0 40 C 40 1 5 3C 14 47 40 40 0 0 40 C 24 1 0 40 60 0 7F 5D 6C 40 5 6 40 0 40 0 A 59 64 6C 0 0 40 C 40 0 40 0 2D 67 9 0 40 6 1C B 40 0 40 0 0 0 1B 34 43 5 40 6 40 7 34 B 40 0 40 0 0 7F 46 0 0 0 7E 7B 3D 63 0 0 0 38 0 3D 0 7E 0 20 0 46 4F 10 1 25 0 5 68 0 0 1 3E 49 36 0 40 0 0 0 40 7 40 0 0 0 0 50 61 6C 6D 20 74 6F 20 42 61 73 73 20 20 52 50 7F 5F 7F 7F 7F 5F 7F 7F 7F 5F 7F 7F 7F 5F 7F 55 23 F7 0 
The sysex length is 187 bytes	
F0 3E 0 0 42 10 0 40 4 0 0 1 40 5 41 2 10 0 41 4 0 3 7 4C 1 40 0 0 D 0 20 48 40 60 0 0 40 C 40 1 5 3C 14 47 40 40 0 0 40 C 24 1 0 40 60 0 7F 5D 6C 40 5 6 40 0 40 0 A 59 64 6C 0 0 40 C 40 0 40 0 2D 67 9 0 40 6 1C B 40 0 40 0 0 0 1B 34 43 5 40 6 40 7 34 B 40 0 40 0 0 7F 46 0 0 0 7E 7B 3D 63 0 0 0 38 0 3D 0 7E 0 20 0 46 4F 10 1 25 0 5 68 0 0 1 3E 49 36 0 40 0 0 0 40 7 40 0 0 0 0 50 61 6C 6D 20 74 6F 20 42 61 73 73 20 20 52 50 7F 5F 7F 7F 7F 5F 7F 7F 7F 5F 7F 7F 7F 5F 7F 55 23 F7 0 
The sysex length is 187 bytes	
F0 3E 0 0 42 10 0 40 4 0 0 1 40 5 41 2 10 0 41 4 0 3 7 4C 1 40 0 0 D 0 20 48 40 60 0 0 40 C 40 1 5 3C 14 47 40 40 0 0 40 C 24 1 0 40 60 0 7F 5D 6C 40 5 6 40 0 40 0 A 59 64 6C 0 0 40 C 40 0 40 0 2D 67 9 0 40 6 1C B 40 0 40 0 0 0 1B 34 43 5 40 6 40 7 34 B 40 0 40 0 0 7F 46 0 0 0 7E 7B 3D 63 0 0 0 38 0 3D 0 7E 0 20 0 46 4F 10 1 25 0 5 68 0 0 1 3E 49 36 0 40 0 0 0 40 7 40 0 0 0 0 50 61 6C 6D 20 74 6F 20 42 61 73 73 20 20 52 50 7F 5F 7F 7F 7F 5F 7F 7F 7F 5F 7F 7F 7F 5F 7F 55 23 F7 0 
The sysex length is 187 bytes	
F0 3E 0 0 42 10 0 40 4 0 0 1 40 5 41 2 10 0 41 4 0 3 7 4C 1 40 0 0 D 0 20 48 40 60 0 0 40 C 40 1 5 3C 14 47 40 40 0 0 40 C 24 1 0 40 60 0 7F 5D 6C 40 5 6 40 0 40 0 A 59 64 6C 0 0 40 C 40 0 40 0 2D 67 9 0 40 6 1C B 40 0 40 0 0 0 1B 34 43 5 40 6 40 7 34 B 40 0 40 0 0 7F 46 0 0 0 7E 7B 3D 63 0 0 0 38 0 3D 0 7E 0 20 0 46 4F 10 1 25 0 5 68 0 0 1 3E 49 36 0 40 0 0 0 40 7 40 0 0 0 0 50 61 6C 6D 20 74 6F 20 42 61 73 73 20 20 52 50 7F 5F 7F 7F 7F 5F 7F 7F 7F 5F 7F 7F 7F 5F 7F 55 23 F7 0 
The sysex length is 187 bytes	
F0 3E 0 0 42 10 0 40 4 0 0 1 40 5 41 2 10 0 41 4 0 3 7 4C 1 40 0 0 D 0 20 48 40 60 0 0 40 C 40 1 5 3C 14 47 40 40 0 0 40 C 24 1 0 40 60 0 7F 5D 6C 40 5 6 40 0 40 0 A 59 64 6C 0 0 40 C 40 0 40 0 2D 67 9 0 40 6 1C B 40 0 40 0 0 0 1B 34 43 5 40 6 40 7 34 B 40 0 40 0 0 7F 46 0 0 0 7E 7B 3D 63 0 0 0 38 0 3D 0 7E 0 20 0 46 4F 10 1 25 0 5 68 0 0 1 3E 49 36 0 40 0 0 0 40 7 40 0 0 0 0 50 61 6C 6D 20 74 6F 20 42 61 73 73 20 20 52 50 7F 5F 7F 7F 7F 5F 7F 7F 7F 5F 7F 7F 7F 5F 7F 55 23 F7 0

The handler function:

Code:
void handleSysEx(const byte* sysExData, uint16_t sysExSize, bool complete)
{
  //simple test to see if the complete message is available
   Serial.print("The sysex length is ");
   Serial.print(sysExSize);
   Serial.print(" bytes\t");
   Serial.println(); 
   for (int i=0;i<=sysExSize;i++){
    
   Serial.print(sysExData[i],HEX);
   Serial.print(" ");
   }
   Serial.println(); 
}

So far so good, but when I request a program dump from the device itself I get inconsistent results:

Code:
The sysex length is 121 bytes	
A 59 64 6C 0 0 40 C 40 0 40 0 2D 67 9 0 40 6 1C B 40 0 40 0 0 0 1B 34 43 5 40 6 40 7 34 B 40 0 40 0 0 7F 46 0 0 0 7E 7B 3D 63 0 0 0 38 0 3D 0 7E 0 20 0 46 4F 10 1 25 0 5 68 0 0 1 3E 49 36 0 40 0 0 0 40 7 40 0 0 0 0 50 61 6C 6D 20 74 6F 20 42 61 73 73 20 20 52 50 7F 5F 7F 7F 7F 5F 7F 7F 7F 5F 7F 7F 7F 5F 7F 55 23 F7 40 
The sysex length is 38 bytes	
0 0 0 0 50 61 6C 6D 20 74 6F 20 42 61 73 73 20 20 52 50 7F 5F 7F 7F 7F 5F 7F 7F 7F 5F 7F 7F 7F 5F 7F 55 23 F7 40 
The sysex length is 131 bytes	
7F 5D 6C 40 5 6 40 0 40 0 A 59 64 6C 0 0 40 C 40 0 40 0 2D 67 9 0 40 6 1C B 40 0 40 0 0 0 1B 34 43 5 40 6 40 7 34 B 40 0 40 0 0 7F 46 0 0 0 7E 7B 3D 63 0 0 0 38 0 3D 0 7E 0 20 0 46 4F 10 1 25 0 5 68 0 0 1 3E 49 36 0 40 0 0 0 40 7 40 0 0 0 0 50 61 6C 6D 20 74 6F 20 42 61 73 73 20 20 52 50 7F 5F 7F 7F 7F 5F 7F 7F 7F 5F 7F 7F 7F 5F 7F 55 23 F7 0 
The sysex length is 64 bytes	
7E 0 20 0 46 4F 10 1 25 0 5 68 0 0 1 3E 49 36 0 40 0 0 0 40 7 40 0 0 0 0 50 61 6C 6D 20 74 6F 20 42 61 73 73 20 20 52 50 7F 5F 7F 7F 7F 5F 7F 7F 7F 5F 7F 7F 7F 5F 7F 55 23 F7 0

My suspicion is that this is the result of some timeout somewhere. The device in question is a hardware synthesizer module made in 1991, it could be that it's so slow that the usb driver triggers a timeout.

So it seems the sysEx callback works out of the box, be it that there's a issue with slow responding devices. Next I will try a minimal Mackie control emulation talking to a DAW and see if that works.

Starting from here what would be the changes needed to support the processing of large messages?

Kind regards,

Gerrit
 
This "i<=sysExSize" should be "i<sysExSize" so that it only prints what was received.

It is curious that in the last batch of output you posted, each message ends with F7, which is correct, but doesn't start with F0.
When the synth sends a program dump, does it send all the programs in one message or each one individually? Any sysex message that is more than USB_MIDI_SYSEX_MAX bytes (you've set it to 255) will be sent in pieces. The first one received will be obvious because it occurs after the request has been sent but it should also begin with F0. The last message will end with F7, but you aren't checking the state of 'complete' which tells you whether this is the last message or not.
Ideally, your handleSysEx function should be checking 'complete' and looking for F0 in the first message and F7 in the last message so that it handles long message correctly.

Pete
 
This "i<=sysExSize" should be "i<sysExSize" so that it only prints what was received.

It is curious that in the last batch of output you posted, each message ends with F7, which is correct, but doesn't start with F0.
When the synth sends a program dump, does it send all the programs in one message or each one individually? Any sysex message that is more than USB_MIDI_SYSEX_MAX bytes (you've set it to 255) will be sent in pieces. The first one received will be obvious because it occurs after the request has been sent but it should also begin with F0. The last message will end with F7, but you aren't checking the state of 'complete' which tells you whether this is the last message or not.
Ideally, your handleSysEx function should be checking 'complete' and looking for F0 in the first message and F7 in the last message so that it handles long message correctly.

Pete

In my last post I used a single program dump message of 187 bytes. What I would like to do is process a complete memory dump of 11527 bytes, extract the program names and store them on the SD card in the display.
I don't understand how it is possible to receive messages large than 255 bytes if the buffer is only 255 bytes, where is the rest kept?


In the meantime I got the Logic Control emulation going using the sysEx callback :)

The code:

Code:
/* 
  Example code for implementing a Logic (Mackie) Control hardware DAW controller

  This example uses the Logic Control communication because this was officially published in the user manual.
  The Mackie Control protocol (essentially the same) never was released officially as far as I know.
  
  This code is in the public domain.
*/
#include <Bounce.h>


// Define pins for buttons
#define stopButtonPin   4
#define playButtonPin   5
#define recordButtonPin 6
#define shiftButtonPin  7
int     bounceTime=5;
// Set up array with 4 buttons
Bounce  controlButton[4]={Bounce(stopButtonPin,bounceTime),Bounce(playButtonPin,bounceTime),
                          Bounce(recordButtonPin,bounceTime),Bounce(shiftButtonPin,bounceTime),};

elapsedMicros   sinceButtonCheck;                         // timer for button check
unsigned int    buttonCheckInterval=2000;                 // interval in microseconds for checking buttons
// enumeration of button names for better readability when referring to buttons in the controlButton array
enum            buttonNames{stopButton,playButton,recordButton,shiftButton};


// Logic Control system exclusive messages
// changed serialnumber of Logic control from 48 to 49
const byte sysExHeader[5]                       = {240,0,0,102,16};
const byte sysExDeviceQuery[7]                  = {0xF0,0x00,0x00,0x66,0x10,0x00,0xF7};
const byte sysExHostConnectionQuery[18]         = {0xF0,0x00,0x00,0x66,0x10,0x01,0x49,0x41,0x31,0x33,0x35,0x36,0x38,0x64,0x7A,0x61,0x41,0xF7};
const byte sysExHostConnectionConfirmation[14]  = {0xF0,0x00,0x00,0x66,0x10,0x03,0x49,0x41,0x31,0x33,0x35,0x36,0x38,0xF7};
const byte sysExVersionReply[12]                = {0xF0,0x00,0x00,0x66,0x10,0x14,0x56,0x31,0x2E,0x30,0x32,0xF7};


void handleSysEx(const byte* sysExData, uint16_t sysExSize, bool complete){
  boolean isMackieControlMessage = true;
  // compare the first five bytes of the message to the sysExHeader to check if it is a Mackie control message
  for (int i=0;i<5;i++) {
   if (sysExData[i]!=sysExHeader[i]) {
      isMackieControlMessage = false;
    }
   }
  // handle mackie control message
  if (isMackieControlMessage) {
    // handle different message types, the 6th byte contains the message type
    switch (sysExData[5]) {
      case 0:
        // respond to device query
        usbMIDI.sendSysEx(18, sysExHostConnectionQuery);
        break;
      case 2:
        // respond to host connection reply with connection confirmation
        usbMIDI.sendSysEx(14, sysExHostConnectionConfirmation);
        break;
      case 19:
        // respond to firmware version request
        usbMIDI.sendSysEx(12, sysExVersionReply);
        break;
      case 18:
        // update LCD display update
        break;
      case 16:
        // update led display
        break;
      case 97:
        //faders to minimum
        break;
      case 98:
        //all leds off
        break;
      case 99:
        //reboot in offline mode
        break;
      default: 
        // statements
      break;
    }
  }
}



void setup() {
  // register sysEx event handler function
  usbMIDI.setHandleSysEx(handleSysEx);
  // define pin modes for button pins:
  pinMode(stopButtonPin,INPUT_PULLUP);
  pinMode(playButtonPin,INPUT_PULLUP);
  pinMode(recordButtonPin,INPUT_PULLUP);
  pinMode(shiftButtonPin,INPUT_PULLUP);


}

void loop() {
  // read usbMIDI, let the handlers do their job
  usbMIDI.read(); 
  // check elapsedMicros and millis timers
  if (sinceButtonCheck >=buttonCheckInterval) {
    sinceButtonCheck = sinceButtonCheck - buttonCheckInterval;
    handleButtons();
  }  
}

// handle soft buttons
void handleButtons(){
  // array for current button state
  int buttonState[4];
  // update buttons and variable            
  for (int i=0;i<4;i++){
    controlButton[i].update();
    buttonState[i]=controlButton[i].read();
  }
  // test if only shift button is pressed
  if (buttonState[stopButton]==HIGH && buttonState[playButton]==HIGH && buttonState[recordButton]==HIGH && buttonState[shiftButton]==LOW){
    // if playButton is pressed and released then send host connection query to DAW
    if( controlButton[playButton].risingEdge()){
      usbMIDI.sendSysEx(18,sysExHostConnectionQuery);
    }
  }
}

If the Teensy is connected (device name is ZeusPC3) and Logic Pro X is started, Logic will scan the ports for new devices. If a compatible control surface is found the dialog will be initiated and the device will be installed. Screenshot of part of the communication:

DAWcommunication.png

The Teensy is recognised as a control surface:

TeensyControl.png

This all happens without me even pushing a button on the teensy. From this point on messages sent by the teensy will no longer be passed on to connected instruments but interpreted as control commands. This should work with any DAW that recognises Logic Control hardware, I'm not sure if the code will respond to Mackie Control queries. To clarify, the Logic Control hardware was made by Mackie and a bit later Mackie released the same hardware as 'Mackie control'.

Kind regards,

Gerrit
 
If you send a large sysex, the driver calls your sysex handler (handleSysEx) each time the buffer fills up to 255 characters. That is why you need to pay attention to the value of the 'complete' argument. If it is false, this current batch of (what should be) 255 characters, is not the end of the message. When 'complete' is true, this buffer is the last one (it won't necessarily be 255 chars long of course) and will have F7 as the last character.
It is up to you to assemble the message into one long byte array.

Pete
 
If you send a large sysex, the driver calls your sysex handler (handleSysEx) each time the buffer fills up to 255 characters. That is why you need to pay attention to the value of the 'complete' argument. If it is false, this current batch of (what should be) 255 characters, is not the end of the message. When 'complete' is true, this buffer is the last one (it won't necessarily be 255 chars long of course) and will have F7 as the last character.
It is up to you to assemble the message into one long byte array.

Pete

The penny dropped! Thank you very much for the clarification. I also understand now why this is a better solution than increasing the buffer, it saves a lot of memory space. Especially in my use case where I only need a small part of the incoming data (just the patch name without all the parameter settings), I don't need to reserve space for something I won't be using.
Here's a simple display handler using a global variable to keep track of the incoming bytes:

Code:
int           sysExIndex=0;     // index for keeping track of incoming sysEx bytes

// handle incoming sysex messages
void handleSysEx(const byte* sysExData, uint16_t sysExSize, bool complete)
{
  // check if this is the start of the sysEx message
  if (sysExData[0]==240){
    sysExIndex=0;
  }
  Serial.print("The sysex length is ");
  Serial.print(sysExSize);
  Serial.print(" bytes\t");
  Serial.print("complete is\t");
  Serial.print(complete);
  Serial.println(); 
  for (int i=0;i<sysExSize;i++){
    sysExIndex++;
    Serial.print(sysExData[i],HEX);
    Serial.print(" ");
  }
  Serial.println(); 
  // message end
  if (complete && sysExData[sysExSize-1]==247){
   Serial.print("The total sysex message length is ");
   Serial.print(sysExIndex);
  Serial.print(" bytes");
  }
}

Now I get the expected result:) Lots of lines scrolling by in the serial monitor ending with:

Code:
....
The sysex length is 255 bytes	complete is	0
20 32 2E 33 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 55 20 0 3D 2 0 0 C 54 0 44 0 30 0 43 2 0 0 C 54 0 44 0 0 0 12 0 40 40 40 0 C 40 3 40 1 0 2F 0 40 40 40 0 C 40 3 40 1 1 40 40 0 7F 7F 40 0 0 C 40 0 40 28 0 68 40 68 0 C 40 0 40 0 40 0 23 0 41 7 40 6 40 9 40 8 40 0 0 0 2D 7F 30 0 40 7 40 6 40 9 40 8 40 0 20 7F 20 40 10 0 0 0 0 0 0 0 0 0 0 0 6 40 9 40 1 0 0 32 0 40 0 7 40 15 0 0 0 0 32 0 40 0 0 0 40 0 40 0 20 1 0 33 33 62 20 50 50 47 20 77 61 76 65 20 32 2E 33 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 55 30 0 39 2 0 0 C 54 0 4A 0 30 0 47 2 0 0 C 54 4 30 0 0 2 0 0 40 40 40 0 C 40 3 63 1 0 0 0 40 40 40 0 C 40 3 40 1 1 40 40 0 7F 7F 40 40 
The sysex length is 255 bytes	complete is	0
0 C 40 0 40 32 0 40 40 7C 0 C 40 0 40 0 40 0 20 7F 21 7 40 6 40 9 40 8 40 0 0 28 11 0 77 0 40 7 40 6 40 9 40 8 40 0 28 7F 28 0 28 0 0 0 0 0 0 0 0 0 0 0 6 40 9 40 1 0 0 2D 0 40 0 7 40 15 1 0 3C 0 32 0 40 0 0 0 40 0 40 0 20 1 0 33 34 61 20 50 50 47 20 20 61 76 65 20 32 2E 33 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 55 20 0 3D 2 0 0 C 54 0 44 0 20 0 43 2 0 0 C 54 0 44 0 0 2 0 0 40 40 40 0 C 40 3 67 1 0 0 0 40 40 40 0 C 40 3 40 1 1 40 40 0 7F 7F 40 40 0 C 40 0 40 F 0 61 40 7C 0 C 40 0 40 0 40 0 32 0 37 7 40 6 40 9 40 8 40 0 0 0 36 0 2A 0 40 7 40 6 40 9 40 8 40 0 20 7F 20 40 10 0 0 0 0 0 0 0 0 0 0 0 6 40 9 40 1 0 0 38 0 40 0 7 40 
The sysex length is 52 bytes	complete is	1
15 1 0 28 0 32 0 40 0 0 0 40 0 40 0 20 1 0 33 36 61 20 50 50 47 20 77 61 76 65 20 32 2E 33 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 55 4C F7 
The total sysex message length is 11527 bytes

Now I can proceed with writing the code for processing the content of the sysEx message.

Would there be any objection to raising the maximum buffer in the library from the current 60 bytes to 255 bytes?

Kind regards,

Gerrit
 
I've succeeded in processing a large program dump message:)
As I will be storing the result to an SD card a buffer array is used to assemble the incoming bytes. The codes determines the incoming message, extracts the patch names and stores them in the buffer.

Code:
// MIDI sysEx messages for requesting program and banks
//  IDW       : Waldorf MIDI ID = 3Eh
//  IDE       : Equipment ID = 00h for MicroWave
//  DEV       : Device number, 00h to 7Eh, 7Fh = global
//  IDM       : Message ID
const byte  mwIversionNumberRequest[7] = {0xF0,0x3E,0x00,0x00,0x00,0x00,0xF7};
const byte  mwIprogramDumpRequest[7] = {0xF0,0x3E,0x00,0x00,0x02,0x00,0xF7};
const byte  mwIprogramBankDumpRequest[7] = {0xF0,0x3E,0x00,0x00,0x10,0x00,0xF7};
const byte  mwIprogramBankDumpHeader[5] = {0xF0,0x3E,0x00,0x00,0x50};

enum        sysExMessageTypes{NOMESSAGE,MWIPROGRAMBANK,MWIMULTIBANK,MWXTPROGRAMBANK,MWXTMULTIBANK,ESQPROGRAMBANK};
int         currentMessage=NOMESSAGE;
int         sysExIndex=0;             // index for keeping track of incoming sysEx bytes
byte        patchNameBuffer[4096];    // buffer for patch names, large enough for receiving 256 16 character names 
int         bufferIndex;              // index for keeping track of buffer

// handle incoming sysex messages
void handleSysEx(const byte* sysExData, uint16_t sysExSize, bool complete)
{
  // check if this is the start of the sysEx message
  if (sysExData[0]==240){
    // reset message & buffer index and clear currentMessage variable
    sysExIndex=0;
    bufferIndex=0;
    currentMessage=NOMESSAGE;
    // determine message type
    bool mw1ProgramBank=true;
    for (int i=1;i<5;i++) {
     if (sysExData[i]!=mwIprogramBankDumpHeader[i]) mw1ProgramBank = false;
    }
    if (mw1ProgramBank){
      // set message type
      currentMessage=MWIPROGRAMBANK;
      Serial.print("Receiving Waldorf Microwave Program Bank Dump...\n");
    }
  }
  // process content
 switch (currentMessage) {
    case MWIPROGRAMBANK:
      // grab program names form dump
      for (int i=0;i<sysExSize;i++){
        sysExIndex++;
        if ((sysExIndex) % 180 >= 154 && (sysExIndex) % 180 <= 169){
            patchNameBuffer[bufferIndex]=sysExData[i];  // store byte in buffer
            bufferIndex++;                              // increase bufferIndex
        }
      }
      // process data if message is complete
      if (complete && sysExData[sysExSize-1]==247){
        Serial.print("Transfer complete\n");
        Serial.print(sysExIndex);
        Serial.print("\t bytes received\n");
        Serial.print(bufferIndex); 
        Serial.print("\t characters in buffer\n\n");
        Serial.print("Buffer content: \n");
        for (int i=0;i<bufferIndex;i++){
          Serial.print(char(patchNameBuffer[i]));
          if ((i+1) % 16==0)Serial.print("\t");
          if ((i+1) % 64==0) Serial.println();
        }
      }
      break;
    default: 
      // print message
      Serial.print("The sysex length is ");
      Serial.print(sysExSize);
      Serial.print(" bytes\t");
      Serial.print("complete is\t");
      Serial.print(complete);Serial.println(); 
      for(int i=0; i<sysExSize; i++){
        Serial.print(sysExData[i], HEX);Serial.print(" ");
      }
      Serial.println(); 
      // message end
      if (complete && sysExData[sysExSize-1]==247){
       Serial.print("The total sysex message length is ");
       Serial.print(sysExIndex);
      Serial.print(" bytes\n");
      }
    break;
  }
}

When the message is complete the content of the buffer is displayed. The result:

Code:
Receiving Waldorf Microwave Program Bank Dump...
Transfer complete
11527	 bytes received
1024	 characters in buffer

Buffer content: 
Pitchio Brass RP	Alien Instr.  RP	Lead 1991     RP	Wald-Piano    RP	
Rob Papen Patent	Fusion-Horns  RP	Lian-Strings  RP	Ring my Bell  RP	
Acid  Bass    RP	Flange-Lead   RP	Tension       RP	Ana Du Lead   RP	
Brain-Pool    RP	Slowvoice     RP	Bor Nepap     RP	Moody Strings RP	
Wave-stratos  RP	SpeakerBass   RP	Reso Nok Nok  RP	Olleewato     RP	
PEPPIE-Lead   RP	Bass by Bob   RP	Velo-Waveseq. RP	Dark-strings  RP	
Mike's Power  RP	Lovethis      RP	Heavy Fuzz    RP	Be bass       RP	
Vangelis      RP	Microharpsi   RP	SleepWave     RP	House seq. YO RP	
Moon-lover    RP	Palm to Bass  RP	MountainFlute RP	Sawseeker Seq.RP	
Byte-Eater    RP	Mega Bass     RP	Flute du PeP  RP	Waldi-Lead    RP	
Wave-strings  RP	Be there seq. RP	Usethis-Bass  RP	PPG-meets Vel.RP	
House-rising  RP	Pulsate wave  RP	Crystal Lake  RP	Dwa Aaaaaa    RP	
Hello Garry   RP	Nip Nap       RP	Orch.Brass    RP	E4+E3 is KLF  RP	
German Synth. RP	Simmons-toms  RP	Down-Bass     RP	TR-Kick       RP	
LovewavePiano RP	Niopalo-Brass RP	Sub-Bass      RP	Jules Verne   RP	
House Wheel   RP	PERU-Lead     RP	MS-20 Bass    RP	Brian-Brass   RP

Now I've got this figured out, handling other devices won't be a problem. Next up; storing the buffer on the SD card.

Kind regards,

Gerrit
 
That looks really good!

Would there be any objection to raising the maximum buffer in the library from the current 60 bytes to 255 bytes?
There's no problem with you increasing your own buffer size. If it was changed in the TeensyDuino distribution, newer Teensys probably won't notice it at all. It might cause grief for some Teensy2 or Teensy++2 apps.

Pete
 
That looks really good!


There's no problem with you increasing your own buffer size. If it was changed in the TeensyDuino distribution, newer Teensys probably won't notice it at all. It might cause grief for some Teensy2 or Teensy++2 apps.

Pete

Thank you:)

I just looked up the specs for the models you mentioned, indeed every byte still counts here.

So far I'm very happy with the results. These tests all were with the SysEx Librarian software sending the file, I still have to check if it also works with the device itself but the code should be able to handle messages broken up into several parts.

Kind regards,

Gerrit
 
Status
Not open for further replies.
Back
Top