Forum Rule: Always post complete source code & details to reproduce any issue!
Results 1 to 24 of 24

Thread: usbMIDI sysex Callback function

  1. #1
    Senior Member
    Join Date
    Jan 2017
    Location
    Maastricht
    Posts
    181

    usbMIDI sysex Callback function

    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...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

  2. #2
    Senior Member oddson's Avatar
    Join Date
    Feb 2013
    Location
    Isle in the Salish Sea
    Posts
    1,410
    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

  3. #3
    Senior Member
    Join Date
    Jan 2017
    Location
    Maastricht
    Posts
    181
    Quote Originally Posted by oddson View Post
    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

  4. #4
    Senior Member
    Join Date
    Nov 2012
    Posts
    1,732
    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

  5. #5
    Senior Member
    Join Date
    Nov 2012
    Posts
    1,732
    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 by el_supremo; 03-10-2017 at 06:54 PM.

  6. #6
    Senior Member
    Join Date
    Nov 2012
    Posts
    1,732
    I get the following error:
    Did you change the Tools|USB Type to MIDI or Serial+MIDI before compiling?

    Pete

  7. #7
    Senior Member
    Join Date
    Jan 2017
    Location
    Maastricht
    Posts
    181
    Quote Originally Posted by el_supremo View Post
    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

  8. #8
    Senior Member
    Join Date
    Nov 2012
    Posts
    1,732
    Post your code.

    Pete

  9. #9
    Senior Member
    Join Date
    Jan 2017
    Location
    Maastricht
    Posts
    181
    Quote Originally Posted by el_supremo View Post
    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

  10. #10
    Senior Member oddson's Avatar
    Join Date
    Feb 2013
    Location
    Isle in the Salish Sea
    Posts
    1,410
    Quote Originally Posted by Gerrit View Post
    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...l=1#post132023

    [edit ] a guess post 4 tells me this would not work.. thanks Pete!
    Last edited by oddson; 03-10-2017 at 10:47 PM.

  11. #11
    Senior Member
    Join Date
    Nov 2012
    Posts
    1,732
    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

  12. #12
    Senior Member
    Join Date
    Nov 2012
    Posts
    1,732
    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

  13. #13
    Senior Member
    Join Date
    Jan 2017
    Location
    Maastricht
    Posts
    181
    Quote Originally Posted by el_supremo View Post
    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 , 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

  14. #14
    Senior Member
    Join Date
    Nov 2012
    Posts
    1,732
    Alas, something is still wrong.
    Have you edited usb_midi.h and usb_midi.cpp and, if so, what have you changed?

    11.4KB file
    Exactly how many bytes are in the file?

    Pete

  15. #15
    Senior Member
    Join Date
    Jan 2017
    Location
    Maastricht
    Posts
    181
    Quote Originally Posted by el_supremo View Post
    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

  16. #16
    Senior Member
    Join Date
    Jan 2017
    Location
    Maastricht
    Posts
    181
    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

  17. #17
    Senior Member
    Join Date
    Nov 2012
    Posts
    1,732
    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

  18. #18
    Senior Member
    Join Date
    Jan 2017
    Location
    Maastricht
    Posts
    181
    Quote Originally Posted by el_supremo View Post
    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:

    Click image for larger version. 

Name:	DAWcommunication.png 
Views:	147 
Size:	134.8 KB 
ID:	9944

    The Teensy is recognised as a control surface:

    Click image for larger version. 

Name:	TeensyControl.png 
Views:	97 
Size:	112.6 KB 
ID:	9945

    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

  19. #19
    Senior Member
    Join Date
    Nov 2012
    Posts
    1,732
    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

  20. #20
    Senior Member
    Join Date
    Nov 2012
    Posts
    1,732
    In the meantime I got the Logic Control emulation going using the sysEx callback
    Excellent. You're getting there

    Pete

  21. #21
    Senior Member
    Join Date
    Jan 2017
    Location
    Maastricht
    Posts
    181
    Quote Originally Posted by el_supremo View Post
    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

  22. #22
    Senior Member
    Join Date
    Jan 2017
    Location
    Maastricht
    Posts
    181
    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

  23. #23
    Senior Member
    Join Date
    Nov 2012
    Posts
    1,732
    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

  24. #24
    Senior Member
    Join Date
    Jan 2017
    Location
    Maastricht
    Posts
    181
    Quote Originally Posted by el_supremo View Post
    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

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •