Skip to content

Commit 311410f

Browse files
committed
Initial commit
1 parent 05e37fd commit 311410f

File tree

3 files changed

+245
-0
lines changed

3 files changed

+245
-0
lines changed

ESP32USBMIDI.ino

Lines changed: 179 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,179 @@
1+
/*
2+
* SPDX-FileCopyrightText: 2019 Ha Thach (tinyusb.org)
3+
*
4+
* SPDX-License-Identifier: MIT
5+
*
6+
* SPDX-FileContributor: 2022-2023 Espressif Systems (Shanghai) CO LTD
7+
*
8+
* SPDX-FileContributor: 2023 [email protected]
9+
*/
10+
11+
// This program is based on an ESP-IDF USB MIDI TinyUSB example with minimal
12+
// changes so it works on Arduino-esp32.
13+
14+
#if ARDUINO_USB_MODE
15+
#warning This sketch must be used when USB is in OTG mode
16+
void setup() {}
17+
void loop() {}
18+
#else
19+
#include "USB.h"
20+
21+
#include "esp32-hal-tinyusb.h"
22+
23+
static const char *TAG = "usbdmidi";
24+
25+
/** TinyUSB descriptors **/
26+
27+
extern "C" uint16_t tusb_midi_load_descriptor(uint8_t *dst, uint8_t *itf) {
28+
uint8_t str_index = tinyusb_add_string_descriptor("TinyUSB MIDI");
29+
uint8_t ep_num = tinyusb_get_free_duplex_endpoint();
30+
TU_VERIFY(ep_num != 0);
31+
uint8_t descriptor[TUD_MIDI_DESC_LEN] = {
32+
// Interface number, string index, EP Out & EP In address, EP size
33+
TUD_MIDI_DESCRIPTOR(*itf, str_index, ep_num, (uint8_t)(0x80 | ep_num),
34+
64)};
35+
*itf += 1;
36+
memcpy(dst, descriptor, TUD_MIDI_DESC_LEN);
37+
return TUD_MIDI_DESC_LEN;
38+
}
39+
40+
// From usb.org MIDI 1.0 specification. This 4 byte structure is the unit
41+
// of transfer for MIDI data over USB.
42+
typedef struct __attribute__((__packed__)) {
43+
uint8_t code_index_number : 4;
44+
uint8_t cable_number : 4;
45+
uint8_t MIDI_0;
46+
uint8_t MIDI_1;
47+
uint8_t MIDI_2;
48+
} USB_MIDI_t;
49+
50+
static void midi_task_read_example(void *arg) {
51+
// The MIDI interface always creates input and output port/jack descriptors
52+
// regardless of these being used or not. Therefore incoming traffic should be
53+
// read (possibly just discarded) to avoid the sender blocking in IO
54+
uint8_t packet[4];
55+
bool read = false;
56+
for (;;) {
57+
delay(1);
58+
while (tud_midi_available()) {
59+
read = tud_midi_packet_read(packet);
60+
if (read) {
61+
ESP_LOGI(TAG,
62+
"Read - Time (ms since boot): %lld, Data: %02hhX %02hhX "
63+
"%02hhX %02hhX",
64+
esp_timer_get_time(), packet[0], packet[1], packet[2],
65+
packet[3]);
66+
USB_MIDI_t *m = (USB_MIDI_t *)packet;
67+
Serial.printf(
68+
"%lld: Cable: %d Code: %01hhX, Data: %02hhX %02hhX %02hhX\n",
69+
esp_timer_get_time(), m->cable_number, m->code_index_number,
70+
m->MIDI_0, m->MIDI_1, m->MIDI_2);
71+
}
72+
}
73+
}
74+
}
75+
76+
// Basic MIDI Messages
77+
#define NOTE_OFF 0x80
78+
#define NOTE_ON 0x90
79+
80+
static void periodic_midi_write_example_cb(void *arg) {
81+
// Example melody stored as an array of note values
82+
uint8_t const note_sequence[] = {
83+
74, 78, 81, 86, 90, 93, 98, 102, 57, 61, 66, 69, 73, 78, 81, 85,
84+
88, 92, 97, 100, 97, 92, 88, 85, 81, 78, 74, 69, 66, 62, 57, 62,
85+
66, 69, 74, 78, 81, 86, 90, 93, 97, 102, 97, 93, 90, 85, 81, 78,
86+
73, 68, 64, 61, 56, 61, 64, 68, 74, 78, 81, 86, 90, 93, 98, 102};
87+
88+
static uint8_t const cable_num = 0; // MIDI jack associated with USB endpoint
89+
static uint8_t const channel = 0; // 0 for channel 1
90+
static uint32_t note_pos = 0;
91+
92+
// Previous positions in the note sequence.
93+
int previous = note_pos - 1;
94+
95+
// If we currently are at position 0, set the
96+
// previous position to the last note in the sequence.
97+
if (previous < 0) {
98+
previous = sizeof(note_sequence) - 1;
99+
}
100+
101+
// Send Note On for current position at full velocity (127) on channel 1.
102+
ESP_LOGI(TAG, "Writing MIDI data %d", note_sequence[note_pos]);
103+
104+
if (tud_midi_mounted()) {
105+
uint8_t note_on[3] = {NOTE_ON | channel, note_sequence[note_pos], 127};
106+
tud_midi_stream_write(cable_num, note_on, 3);
107+
108+
// Send Note Off for previous note.
109+
uint8_t note_off[3] = {NOTE_OFF | channel, note_sequence[previous], 0};
110+
tud_midi_stream_write(cable_num, note_off, 3);
111+
}
112+
113+
// Increment position
114+
note_pos++;
115+
116+
// If we are at the end of the sequence, start over.
117+
if (note_pos >= sizeof(note_sequence)) {
118+
note_pos = 0;
119+
}
120+
}
121+
122+
void app_main(void) {
123+
// Periodically send MIDI packets
124+
int const tempo = 286;
125+
const esp_timer_create_args_t periodic_midi_args = {
126+
.callback = &periodic_midi_write_example_cb,
127+
/* name is optional, but may help identify the timer when debugging */
128+
.name = "periodic_midi"};
129+
130+
ESP_LOGI(TAG, "MIDI write task init");
131+
esp_timer_handle_t periodic_midi_timer;
132+
ESP_ERROR_CHECK(esp_timer_create(&periodic_midi_args, &periodic_midi_timer));
133+
ESP_ERROR_CHECK(esp_timer_start_periodic(periodic_midi_timer, tempo * 1000));
134+
135+
// Read recieved MIDI packets
136+
ESP_LOGI(TAG, "MIDI read task init");
137+
xTaskCreate(midi_task_read_example, "midi_task_read_example", 2 * 1024, NULL,
138+
5, NULL);
139+
}
140+
141+
static void usbEventCallback(void *arg, esp_event_base_t event_base,
142+
int32_t event_id, void *event_data) {
143+
if (event_base == ARDUINO_USB_EVENTS) {
144+
arduino_usb_event_data_t *data = (arduino_usb_event_data_t *)event_data;
145+
switch (event_id) {
146+
case ARDUINO_USB_STARTED_EVENT:
147+
Serial.println("USB PLUGGED");
148+
break;
149+
case ARDUINO_USB_STOPPED_EVENT:
150+
Serial.println("USB UNPLUGGED");
151+
break;
152+
case ARDUINO_USB_SUSPEND_EVENT:
153+
Serial.printf("USB SUSPENDED: remote_wakeup_en: %u\n",
154+
data->suspend.remote_wakeup_en);
155+
break;
156+
case ARDUINO_USB_RESUME_EVENT:
157+
Serial.println("USB RESUMED");
158+
break;
159+
160+
default:
161+
break;
162+
}
163+
}
164+
}
165+
166+
void setup() {
167+
Serial.begin(115200);
168+
169+
USB.onEvent(usbEventCallback);
170+
tinyusb_enable_interface(USB_INTERFACE_MIDI, TUD_MIDI_DESC_LEN,
171+
tusb_midi_load_descriptor);
172+
USB.begin();
173+
while (!Serial && millis() < 5000)
174+
delay(10);
175+
app_main();
176+
}
177+
178+
void loop() {}
179+
#endif /* ARDUINO_USB_MODE */

LICENSE

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
/*
2+
* MIT License
3+
*
4+
* Copyright (c) 2023 [email protected]
5+
*
6+
* Permission is hereby granted, free of charge, to any person obtaining a copy
7+
* of this software and associated documentation files (the "Software"), to deal
8+
* in the Software without restriction, including without limitation the rights
9+
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10+
* copies of the Software, and to permit persons to whom the Software is
11+
* furnished to do so, subject to the following conditions:
12+
*
13+
* The above copyright notice and this permission notice shall be included in all
14+
* copies or substantial portions of the Software.
15+
*
16+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17+
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18+
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19+
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20+
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21+
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22+
* SOFTWARE.
23+
*/

README.md

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
# ESP32-S2 and ESP32-S3 USB MIDI Example for Arduino
2+
3+
This program is based on an ESP-IDF TinyUSB USB MIDI example with minimal
4+
changes so it works with arduino-esp32. See https://github.com/espressif/esp-idf/blob/master/examples/peripherals/usb/device/tusb_midi/main/tusb_midi_main.c
5+
for the original code.
6+
7+
The board must be reset or power cycled to make the new code take effect. This
8+
has been tested on ESP32-S3 and ESP32-S2 DevKit boards ("ESP32S3 Dev Module"
9+
and "ESP32S2 Dev Module").
10+
11+
USB Mode must be set to USB-OTG (TinyUSB) on ESP32S3.
12+
13+
For lots more debug output, set the Debug Core Level to Verbose. It helps to be
14+
familiar with the ESP-IDF API.
15+
16+
Example of output on the Arduino IDE Serial monitor.
17+
The computer connected to the ESP32S3 sends MIDI Start, Stop, and CC 7 0.
18+
The function midi_task_read_example() processes the incoming MIDI data and
19+
prints the following.
20+
```
21+
141236968: Cable: 0 Code: F, Data: FA 00 00
22+
141237070: Cable: 0 Code: F, Data: FC 00 00
23+
141237158: Cable: 0 Code: B, Data: B0 07 00
24+
```
25+
26+
Example of MIDI received from the ESP32S3.
27+
The function periodic_midi_write_example_cb() sends MIDI data. The computer
28+
connected to the ESP32S3 produced the following output.
29+
```
30+
channel 1 note-on F#4 127
31+
channel 1 note-off D4 0
32+
channel 1 note-on A4 127
33+
channel 1 note-off F#4 0
34+
channel 1 note-on D5 127
35+
channel 1 note-on F#4 127
36+
channel 1 note-off D4 0
37+
channel 1 note-on A4 127
38+
channel 1 note-off F#4 0
39+
channel 1 note-on D5 127
40+
channel 1 note-off A4 0
41+
channel 1 note-on F#5 127
42+
channel 1 note-off D5 0
43+
```

0 commit comments

Comments
 (0)