Je viens de regarder un vieux test que j'avais effectué avec un "Funduino USB Host Shield" connecté en SPI sur un Arduino avec un clavier midi
Clavier Midi <-- USB --> USB Host Shield <--- SPI + qques pin ---> Arduino <-- USB --> Mac
il est assez simple de décoder les messages en provenance du clavier, j'ai retrouvé mon code de test et en branchant un vieux clavier midi (M-Audio Keystation 61 es)
// installer USB_Host_Shield_Library_2.0 => https://github.com/felis/USB_Host_Shield_2.0
#include <usbh_midi.h>
#include <usbhub.h>
USB Usb;
USBHub Hub(&Usb);
USBH_MIDI usbMidi(&Usb);
enum t_Status : uint8_t {
s_unknown = 0b0000, // not in the spec
s_NoteOff = 0b1000, // Key released
s_NoteOn = 0b1001, // Key pressed
s_AfterTouch = 0b1010, // Key pressure is changing
s_Controller = 0b1011, // CC/controller motion
s_ProgramChange = 0b1100, // Patch/bank or program change
s_ChannelPressure = 0b1101, // Average key pressure is changing
s_PitchBend = 0b1110, // Pitch wheel motion
s_System = 0b1111, // SysEx message of some sort
};
void printNibble(uint8_t aNibble) {
for (int8_t i = 3; i >= 0; i--) Serial.write(bitRead(aNibble, i) ? '1' : '0');
}
void printByte2(uint8_t aByte) {
for (int8_t i = 7; i >= 0; i--) Serial.write(bitRead(aByte, i) ? '1' : '0');
}
void printByte16(uint8_t aByte) {
if (aByte < 0x10) Serial.write('0');
Serial.print(aByte, HEX);
Serial.write(' ');
}
void printMidiMessage(uint8_t midiMessage[], uint8_t messageLength) {
if (messageLength != 0) {
for (uint8_t i = 0; i < messageLength; i++) {
printByte2(midiMessage[i]);
Serial.write(' ');
}
Serial.println();
}
}
/*
----------------------------------------------------------------------------------
Status Byte 1 Byte 2 Message Legend
----------------------------------------------------------------------------------
1000nnnn 0kkkkkkk 0vvvvvvv Note Off n=channel k=key v=velocity
1001nnnn 0kkkkkkk 0vvvvvvv Note On n=channel k=key v=velocity
1010nnnn 0kkkkkkk 0ppppppp AfterTouch n=channel k=key p=pressure
1011nnnn 0ccccccc 0vvvvvvv Controller Value n=channel c=controller v=value
1100nnnn 0ppppppp [none] Program Change n=channel p=preset
1101nnnn 0ppppppp [none] Channel Pressure n=channel p=pressure
1110nnnn 0fffffff 0ccccccc Pitch Bend n=channel c=coarse f=fine (14 bit)
1111ssss SysEx message s = see below
----------------------------------------------------------------------------------
SysExStart = 0xF0, // Start of SysEx stream
SysExEnd = 0xF7, // End of SysEx stream
MTCQuaterFrame = 0xF1, // MTC quarter frame time code
SongPosition = 0xF2, // Ask slave to position playback cue
SongSelect = 0xF3, // Select a certain song and cue to beginning
TuningRequested = 0xF6, // Being asked to self-tune
MidiClock = 0xF8, // Being kept in sync with a tempo (24 clocks per quarter note)
MidiTick = 0xF9, // Being kept in sync with a tick (every 10ms)
MidiStart = 0xFA, // Master asking for playback from the beginning
MidiContinue = 0xFB, // Master asked that we continue playback from cue
MidiStop = 0xFC, // Master asked to stop playback and retain cue point
ActiveSense = 0xFE, // Keepalive data to let us know things are still connected - i.e. "nervous" devices
Reset = 0xFF, // Reset to default, just turned on, no keys pressed, cue to beginning etc
*/
void decodeMidiMessage(uint8_t midiMessage[], uint8_t messageLength) {
t_Status statusNibble = s_unknown;
uint8_t channel = 0;
if (messageLength != 0) {
statusNibble = (t_Status) (midiMessage[0] >> 4); // message type
channel = midiMessage[0] & 0b1111; // channel information
Serial.print(F("Channel = ")); Serial.print(channel); Serial.write('\t');
switch (statusNibble) {
case s_NoteOff: // Key released
Serial.print(F("Key released.\tkey: ")); Serial.print(midiMessage[1]);
Serial.print(F("\tVelocity: ")); Serial.println(midiMessage[2]);
break;
case s_NoteOn: // Key pressed
if (midiMessage[2] == 0) Serial.print(F("Key released.\tkey: "));
else Serial.print(F("Key pressed.\tkey: "));
Serial.print(midiMessage[1]);
Serial.print(F("\tVelocity: ")); Serial.println(midiMessage[2]);
break;
case s_AfterTouch: // Key pressure is changing
Serial.print(F("Key pressure.\tkey: ")); Serial.print(midiMessage[1]);
Serial.print(F("\tPressure: ")); Serial.println(midiMessage[2]);
break;
case s_Controller: // CC/controller motion
Serial.print(F("Motion.\tController: ")); Serial.print(midiMessage[1]);
Serial.print(F("\tValue: ")); Serial.println(midiMessage[2]);
break;
case s_ProgramChange: // Patch/bank or program change
Serial.print(F("program change.\tPreset: ")); Serial.println(midiMessage[1]);
break;
case s_ChannelPressure: // Average key pressure is changing
Serial.print(F("Channel pressure.\tValue: ")); Serial.println(midiMessage[1]);
break;
case s_PitchBend: // Pitch wheel motion
Serial.print(F("Pitch wheel.\tFine: ")); Serial.print(midiMessage[1]);
Serial.print(F("\tCoarse: ")); Serial.println(midiMessage[2]);
break;
case s_System: // SysEx message of some sort
Serial.println(F("SysEx message (ignored)."));
break;
default:
Serial.println(F("unknown status (ignored)."));
break;
}
}
}
void getMidi() {
uint8_t midiMessage[3];
uint8_t messageLength = usbMidi.RecvData(midiMessage);
decodeMidiMessage(midiMessage, messageLength);
}
void onInit()
{
uint16_t vid = usbMidi.idVendor();
uint16_t pid = usbMidi.idProduct();
Serial.print("Vendor ID: 0x"); Serial.print(vid, HEX);
Serial.print(", Product ID: 0x"); Serial.println(pid, HEX);
Serial.println();
}
void setup()
{
Serial.begin(115200);
if (Usb.Init() == -1) {
Serial.println(F("Usb.Init error, halting."));
while (true) yield(); //halt
}
delay(200);
usbMidi.attachOnInit(onInit); // Register onInit() function
Serial.println(F("Prêt"));
}
void loop()
{
Usb.Task();
if (usbMidi) getMidi();
}
quand on appuie et relâche une touche du clavier j'ai ça
Channel = 0 Key pressed. key: 50 Velocity: 41
Channel = 0 Key released. key: 50 Velocity: 0
Quand je bouge le pitch:
Channel = 0 Pitch wheel. Fine: 0 Coarse: 63
Channel = 0 Pitch wheel. Fine: 0 Coarse: 62
Channel = 0 Pitch wheel. Fine: 0 Coarse: 61
Channel = 0 Pitch wheel. Fine: 0 Coarse: 60
Channel = 0 Pitch wheel. Fine: 0 Coarse: 59
Channel = 0 Pitch wheel. Fine: 0 Coarse: 58
Channel = 0 Pitch wheel. Fine: 0 Coarse: 57
Channel = 0 Pitch wheel. Fine: 0 Coarse: 56
...
quand je bouge la Modulation
Channel = 0 Motion. Controller: 1 Value: 42
Channel = 0 Motion. Controller: 1 Value: 41
Channel = 0 Motion. Controller: 1 Value: 40
Channel = 0 Motion. Controller: 1 Value: 39
Channel = 0 Motion. Controller: 1 Value: 38
Channel = 0 Motion. Controller: 1 Value: 37
Channel = 0 Motion. Controller: 1 Value: 36
Channel = 0 Motion. Controller: 1 Value: 35
Channel = 0 Motion. Controller: 1 Value: 34
...
quand je bouge le volume
Channel = 0 Motion. Controller: 7 Value: 122
Channel = 0 Motion. Controller: 7 Value: 121
Channel = 0 Motion. Controller: 7 Value: 120
Channel = 0 Motion. Controller: 7 Value: 119
Channel = 0 Motion. Controller: 7 Value: 118
Channel = 0 Motion. Controller: 7 Value: 117
Channel = 0 Motion. Controller: 7 Value: 116
Channel = 0 Motion. Controller: 7 Value: 115
Channel = 0 Motion. Controller: 7 Value: 114
...
etc
En branchant un module USB Host sur un Arduino Micro, et en modifiant un peu la partie décodage de mon code, on peut directement envoyer un mouvement de souris ou des touches clavier à l'ordinateur au Travers du câble USB.
Le shield qui a le format "UNO" a besoin des 2 GND connectés, reset, 3.3V, 5V, le port ICSP (SCK, MOSI, MISO; RESET) ainsi que les pins 9 et 10 (9 sert pour le INT) et 10 est le SS qui fait aussi partie du port ICSP.