I understand your concern but, in my opinion, encoders are widely used in robotics and deserve an optimized protocol even if it requires to allocate a one of the few available midi message types.

I think your idea to pack `CHANNEL`(encoder number) and `DIRECTION` into one byte is great (for current SYSEX response) but I don't know how to do that.
Could you please explain how to : 
* Compute byte from `CHANNEL`(encoder number) and `DIRECTION`
* Compute `CHANNEL`(encoder number) and `DIRECTION` from received byte

I propose to update protocol this way : 
Replace
```c
/*  Report encoder's position
 * -----------------------------------------------------
 * Sysex Query
 * ------------------
 * 0 START_SYSEX                (0xF0)
 * 1 ENCODER_DATA               (0x61)
 * 2 ENCODER_REPORT_POSITION    (0x01)
 * 3 encoder #                  ([0 - MAX_ENCODERS-1])
 * 4 END_SYSEX                  (0xF7)
 *--------------------
 * MIDI Response 
 * ------------------
 * 0 ENCODER_MESSAGE (0x80) | Channel (encoder #, 0-4)
 * 1 direction (positive = 0, negative = 1)
 * 2 current position, bits 0-6
 * 3 current position, bits 7-13
 * 4 current position, bits 14-20
 * 5 current position, bits 21-27
 * -----------------------------------------------------
 * 
 * Report all encoders positions
 * -----------------------------------------------------
 * Sysex Query
 * ------------------
 * 0 START_SYSEX                (0xF0)
 * 1 ENCODER_DATA               (0x61)
 * 2 ENCODER_REPORT_POSITIONS   (0x02)
 * 3 END_SYSEX                  (0xF7)
 *--------------------
 * Sysex Response
 * ------------------
 * 0 START_SYSEX                (0xF0)
 * 1 ENCODER_DATA               (0x61)
 * 2 first encoder #            ([0 - MAX_ENCODERS-1])
 * 3 first enc. dir.            (positive = 0, negative = 1)
 * 4 first enc. position, bits 0-6
 * 5 first enc. position, bits 7-13
 * 6 first enc. position, bits 14-20
 * 7 first enc. position, bits 21-27
 * ...
 * N END_SYSEX                  (0xF7)
 * -----------------------------------------------------
 */
```

by

```c
/*  Report encoder's position
 * -----------------------------------------------------
 * Sysex Query
 * ------------------
 * 0 START_SYSEX                (0xF0)
 * 1 ENCODER_DATA               (0x61)
 * 2 ENCODER_REPORT_POSITION    (0x01)
 * 3 encoder #                  ([0 - MAX_ENCODERS-1])
 * 4 END_SYSEX                  (0xF7)
 *--------------------
 * MIDI Response       =>  keep it this way from now, waiting for your decision
 * ------------------
 * 0 ENCODER_MESSAGE (0x80) | Channel (encoder #, 0-4)
 * 1 direction (positive = 0, negative = 1)
 * 2 current position, bits 0-6
 * 3 current position, bits 7-13
 * 4 current position, bits 14-20
 * 5 current position, bits 21-27
 * -----------------------------------------------------
 * 
 * Report all encoders positions
 * -----------------------------------------------------
 * Sysex Query
 * ------------------
 * 0 START_SYSEX                (0xF0)
 * 1 ENCODER_DATA               (0x61)
 * 2 ENCODER_REPORT_POSITIONS   (0x02)
 * 3 END_SYSEX                  (0xF7)
 *--------------------
 * Sysex Response
 * ------------------
 * 0 START_SYSEX                (0xF0)
 * 1 ENCODER_DATA               (0x61)
 * 2 first encoder #  &  first enc. dir.       => pack CHANNEL and DIRECTION into the same byte
 * 3 first enc. position, bits 0-6
 * 4 first enc. position, bits 7-13
 * 5 first enc. position, bits 14-20
 * 6 first enc. position, bits 21-27
 * ...
 * N END_SYSEX                  (0xF7)
 * -----------------------------------------------------
 */
```