Skip to content

Commit 8ffe438

Browse files
bwhitmanclaude
andcommitted
AMYBOARD_USB_HOST: one-off variant with USB MIDI host + UART REPL
New board variant for AMYboard that runs the USB port as a USB MIDI *host* (like Tulip CC) instead of the TinyUSB CDC + gadget-MIDI device. The REPL moves to UART0 — the same pins the stderr console already uses — so you control the board with a USB-serial dongle on the UART header while a MIDI keyboard/controller plugs into the USB port. - boards/AMYBOARD_USB_HOST: BOARD_DEFINITION1=AMYBOARD_USB_HOST, compiles ../esp32s3/usb_host.c, UART REPL on; otherwise identical config to AMYBOARD (same sdkconfig list and partition table). - esp32s3/usb_host.c/h: new USB_HOST_MIDI_ONLY guards (defined when AMYBOARD) compile out all HID keyboard/mouse/lvgl handling, leaving the MIDI host in/out paths. TULIP builds are unchanged. - amyboard/main.c: variant skips TinyUSB device init and starts the run_usb host task instead; defines tulip_ready (set in tulip_amyboard_start once AMY is up, gating host MIDI -> AMY). - amy_connector.c: variant drops AMY_MIDI_IS_USB_GADGET (the TinyUSB device stack is compiled for amy_midi.c's tud_* references but never initialized — all tud_* calls are verified no-ops in that state) and enables the Tulip send_usb_midi_out path for MIDI out to the attached USB device. Built and verified: AMYBOARD_USB_HOST, stock AMYBOARD, and TULIP4_R11 all compile clean. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
1 parent 8cfa638 commit 8ffe438

10 files changed

Lines changed: 151 additions & 22 deletions

File tree

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
{
2+
"deploy": [
3+
"../deploy_s3.md"
4+
],
5+
"docs": "",
6+
"features": [
7+
"BLE",
8+
"WiFi"
9+
],
10+
"images": [
11+
"generic_s3.jpg"
12+
],
13+
"mcu": "esp32s3",
14+
"product": "AMYboard (USB MIDI host)",
15+
"thumbnail": "",
16+
"url": "https://www.espressif.com/en/products/modules",
17+
"vendor": "Espressif"
18+
}
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
set(IDF_TARGET esp32s3)
2+
3+
set(MICROPY_PY_TINYUSB ON)
4+
5+
6+
# AMYBOARD is always defined for this port (see esp32_common.cmake); this adds
7+
# the variant macro that switches USB to host (MIDI) mode + UART REPL.
8+
set(BOARD_DEFINITION1 AMYBOARD_USB_HOST)
9+
set(BOARD_DEFINITION2 MAKERFABS)
10+
11+
set(SDKCONFIG_DEFAULTS
12+
../../micropython/ports/esp32/boards/sdkconfig.base
13+
../../micropython/ports/esp32/boards/sdkconfig.usb
14+
../../micropython/ports/esp32/boards/sdkconfig.240mhz
15+
../esp32s3/boards/sdkconfig.tulip
16+
boards/AMYBOARD/sdkconfig.board
17+
)
18+
19+
list(APPEND MICROPY_SOURCE_BOARD
20+
../esp32s3/usb_host.c
21+
)
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
// AMYboard variant: the USB port runs as a USB MIDI *host* (like Tulip CC)
2+
// instead of a TinyUSB CDC + gadget-MIDI device. The REPL moves to UART0 —
3+
// the same pins the stderr console already uses — so connect a USB-serial
4+
// dongle to the UART header to control the board.
5+
//
6+
// The TinyUSB device stack is still compiled (amy_midi.c references tud_midi_*
7+
// whenever AMYBOARD is defined) but usb_init()/tusb_init() are never called,
8+
// so every tud_* call is a safe no-op.
9+
#include "../AMYBOARD/mpconfigboard.h"
10+
11+
#undef MICROPY_HW_BOARD_NAME
12+
#define MICROPY_HW_BOARD_NAME "AMYboard USB host"
13+
14+
#undef MICROPY_HW_ENABLE_UART_REPL
15+
#define MICROPY_HW_ENABLE_UART_REPL (1)
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
I2C_SCL,GPIO9
2+
I2C_SDA,GPIO8
3+
FG_INT,GPIO21
4+
UART0_TX,GPIO43
5+
UART0_RX,GPIO44

tulip/amyboard/main.c

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,15 @@ TaskHandle_t idle_1_handle;
9797
TaskHandle_t sequencer_handle;
9898
TaskHandle_t cv_read_handle;
9999

100+
#ifdef AMYBOARD_USB_HOST
101+
// USB runs in host (MIDI) mode instead of TinyUSB device mode. usb_host.c
102+
// gates MIDI input to AMY on tulip_ready; it's set once run_amy() has started
103+
// AMY (see tulip_amyboard_start in modtulip.c).
104+
TaskHandle_t usb_handle;
105+
uint8_t tulip_ready = 0;
106+
extern void run_usb();
107+
#endif
108+
100109
// For CPU usage
101110
unsigned long last_task_counters[MAX_TASKS];
102111

@@ -201,7 +210,10 @@ void mp_task(void *pvParameter) {
201210
#if MICROPY_PY_THREAD
202211
mp_thread_init(pxTaskGetStackStart(NULL), MICROPY_TASK_STACK_SIZE / sizeof(uintptr_t));
203212
#endif
204-
#if MICROPY_HW_ESP_USB_SERIAL_JTAG
213+
#ifdef AMYBOARD_USB_HOST
214+
// USB is a MIDI host on this variant (started in app_main); never bring up
215+
// the TinyUSB device stack. The REPL is on UART0 instead (with stderr).
216+
#elif MICROPY_HW_ESP_USB_SERIAL_JTAG
205217
usb_serial_jtag_init();
206218
#elif MICROPY_HW_ENABLE_USBDEV
207219
usb_init();
@@ -351,6 +363,13 @@ void app_main(void) {
351363
extern void cv_read_task(void *pvParameter);
352364
xTaskCreatePinnedToCore(cv_read_task, CV_READ_TASK_NAME, CV_READ_TASK_STACK_SIZE / sizeof(StackType_t), NULL, CV_READ_TASK_PRIORITY, &cv_read_handle, CV_READ_TASK_COREID);
353365

366+
#ifdef AMYBOARD_USB_HOST
367+
fprintf(stderr,"Starting USB host on core %d\n", USB_TASK_COREID);
368+
xTaskCreatePinnedToCore(run_usb, USB_TASK_NAME, (USB_TASK_STACK_SIZE) / sizeof(StackType_t), NULL, USB_TASK_PRIORITY, &usb_handle, USB_TASK_COREID);
369+
fflush(stderr);
370+
delay_ms(100);
371+
#endif
372+
354373
fprintf(stderr,"Starting MicroPython on core %d\n", TULIP_MP_TASK_COREID);
355374
xTaskCreatePinnedToCore(mp_task, TULIP_MP_TASK_NAME, (TULIP_MP_TASK_STACK_SIZE) / sizeof(StackType_t), NULL, TULIP_MP_TASK_PRIORITY, &tulip_mp_handle, TULIP_MP_TASK_COREID);
356375
fflush(stderr);

tulip/amyboard/tasks.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,12 @@
1515
#define CV_READ_TASK_STACK_SIZE (2 * 1024)
1616
#define CV_READ_TASK_NAME "cv_read_task"
1717

18+
// USB MIDI host task (AMYBOARD_USB_HOST variant only) — same shape as Tulip's.
19+
#define USB_TASK_PRIORITY (ESP_TASK_PRIO_MIN + 1)
20+
#define USB_TASK_COREID (1)
21+
#define USB_TASK_STACK_SIZE (8 * 1024)
22+
#define USB_TASK_NAME "usb_task"
23+
1824
#define MAX_TASKS 17 // includes system tasks
1925

2026
extern TaskHandle_t tulip_mp_handle;

tulip/esp32s3/usb_host.c

Lines changed: 38 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -107,10 +107,27 @@ void show_endpoint_desc(const void *p, int indent)
107107

108108
#endif // DEBUG_USB
109109

110+
#define DEFAULT_TIMEOUT_MS (5000)
111+
112+
const TickType_t HOST_EVENT_TIMEOUT = 1;
113+
const TickType_t CLIENT_EVENT_TIMEOUT = 1;
114+
115+
const size_t USB_HID_DESC_SIZE = 9;
116+
117+
usb_host_client_handle_t Client_Handle;
118+
usb_device_handle_t Device_Handle_midi;
119+
120+
uint8_t Interface_Number_midi;
121+
122+
bool midi_claimed = false;
123+
bool midi_has_out = false;
124+
bool midi_has_in = false;
125+
bool midi_ready = false;
126+
127+
#ifndef USB_HOST_MIDI_ONLY
110128
#define KEYBOARD_BUFFER_SIZE 64
111129
#define KEYBOARD_BYTES 8
112130
#define MOUSE_BYTES 8
113-
#define DEFAULT_TIMEOUT_MS (5000)
114131
uint16_t keyboard_bytes = KEYBOARD_BYTES;
115132
uint16_t mouse_bytes = MOUSE_BYTES;
116133

@@ -119,29 +136,15 @@ uint16_t mouse_bytes = MOUSE_BYTES;
119136
// How often (in ms) to repeat a key once held
120137
#define KEY_REPEAT_INTER_MS 90
121138

122-
123-
const TickType_t HOST_EVENT_TIMEOUT = 1;
124-
const TickType_t CLIENT_EVENT_TIMEOUT = 1;
125-
126-
const size_t USB_HID_DESC_SIZE = 9;
127-
128-
usb_host_client_handle_t Client_Handle;
129139
usb_device_handle_t Device_Handle_kb;
130-
usb_device_handle_t Device_Handle_midi;
131140
usb_device_handle_t Device_Handle_mouse;
132141

133142
uint8_t Interface_Number_kb;
134-
uint8_t Interface_Number_midi;
135143
uint8_t Interface_Number_mouse;
136144

137145
bool keyboard_claimed = false;
138146
bool keyboard_ready = false;
139147

140-
bool midi_claimed = false;
141-
bool midi_has_out = false;
142-
bool midi_has_in = false;
143-
bool midi_ready = false;
144-
145148
bool mouse_claimed = false;
146149
bool mouse_ready = false;
147150

@@ -162,6 +165,7 @@ int64_t KeyRepeatTimer=0;
162165
uint16_t current_held = 0;
163166
int64_t current_held_ms = 0;
164167
int64_t last_inter_trigger_ms = 0;
168+
#endif // !USB_HOST_MIDI_ONLY
165169

166170

167171
#define MIDI_IN_BUFFERS 8
@@ -171,7 +175,8 @@ usb_transfer_t *MIDIOut = NULL;
171175
usb_transfer_t *MIDIIn[MIDI_IN_BUFFERS] = { NULL };
172176

173177

174-
// This identifies a mouse HID report packet for a standard "boot" mouse.
178+
#ifndef USB_HOST_MIDI_ONLY
179+
// This identifies a mouse HID report packet for a standard "boot" mouse.
175180
typedef struct {
176181
union {
177182
struct {
@@ -189,6 +194,7 @@ typedef struct {
189194
int16_t mouse_x_pos = 0;
190195
int16_t mouse_y_pos = 0;
191196
uint8_t mouse_buttons[3];
197+
#endif // !USB_HOST_MIDI_ONLY
192198

193199

194200

@@ -494,6 +500,7 @@ void _client_event_callback(const usb_host_client_event_msg_t *event_msg, void *
494500
err = usb_host_device_close(Client_Handle, Device_Handle_midi);
495501
if (err != ESP_OK) fprintf(stderr,"usb_host_device_close err: 0x%x\n", err);
496502
}
503+
#ifndef USB_HOST_MIDI_ONLY
497504
if (keyboard_claimed && (uint32_t)Device_Handle_kb == (uint32_t)event_msg->dev_gone.dev_hdl) {
498505
err = usb_host_interface_release(Client_Handle, Device_Handle_kb, Interface_Number_kb);
499506
if (err != ESP_OK) fprintf(stderr,"usb_host_interface_release err: 0x%x\n", err);
@@ -511,6 +518,7 @@ void _client_event_callback(const usb_host_client_event_msg_t *event_msg, void *
511518
err = usb_host_device_close(Client_Handle, Device_Handle_mouse);
512519
if (err != ESP_OK) fprintf(stderr,"usb_host_device_close err: 0x%x\n", err);
513520
}
521+
#endif // !USB_HOST_MIDI_ONLY
514522
break;
515523
default:
516524
fprintf(stderr,"Unknown USB event: %d\n", event_msg->event);
@@ -567,6 +575,8 @@ void usbh_task(void)
567575

568576

569577

578+
#ifndef USB_HOST_MIDI_ONLY
579+
570580
static char lvgl_kb_buf[KEYBOARD_BUFFER_SIZE];
571581

572582
void lvgl_keyboard_read(lv_indev_t * indev_drv, lv_indev_data_t * data) {
@@ -885,6 +895,8 @@ void prepare_endpoint_hid_mouse(const void *p)
885895
//}
886896
}
887897

898+
#endif // !USB_HOST_MIDI_ONLY
899+
888900

889901
void new_enumeration_config_fn(const usb_config_desc_t *config_desc, usb_device_handle_t Device_Handle) {
890902
// We just retrieved the config of a newly-connected device.
@@ -922,18 +934,22 @@ void new_enumeration_config_fn(const usb_config_desc_t *config_desc, usb_device_
922934
|| (last_descriptor == USB_B_DESCRIPTOR_TYPE_ENDPOINT)) --indent;
923935
show_interface_desc(p, indent++);
924936
if(!midi_claimed) { check_interface_desc_MIDI(p, Device_Handle); }
937+
#ifndef USB_HOST_MIDI_ONLY
925938
if(!keyboard_claimed) { check_interface_desc_boot_keyboard(p, Device_Handle); }
926939
if(!mouse_claimed) { check_interface_desc_boot_mouse(p, Device_Handle); }
940+
#endif
927941
last_descriptor = bDescriptorType;
928942
break;
929943
case USB_B_DESCRIPTOR_TYPE_ENDPOINT:
930944
show_endpoint_desc(p, indent);
945+
#ifndef USB_HOST_MIDI_ONLY
931946
if (keyboard_claimed && !keyboard_ready) {
932947
prepare_endpoint_hid_kb(p);
933948
}
934949
if (mouse_claimed && !mouse_ready) {
935950
prepare_endpoint_hid_mouse(p);
936951
}
952+
#endif
937953
if (midi_claimed && !midi_ready) {
938954
prepare_endpoint_midi(p);
939955
}
@@ -972,15 +988,18 @@ void new_enumeration_config_fn(const usb_config_desc_t *config_desc, usb_device_
972988

973989
void run_usb()
974990
{
991+
#ifndef USB_HOST_MIDI_ONLY
975992
// Reset key maps
976993
for(uint8_t i=0;i<MAX_KEY_REMAPS;i++) {
977994
key_remaps[i].scan = 0;
978995
key_remaps[i].mod = 0;
979996
key_remaps[i].code = 0;
980997
}
998+
#endif
981999
usbh_setup();
9821000
while(1) {
9831001
usbh_task();
1002+
#ifndef USB_HOST_MIDI_ONLY
9841003
KeyboardTimer = esp_timer_get_time() / 1000;
9851004
MouseTimer = esp_timer_get_time() / 1000;
9861005
KeyRepeatTimer = esp_timer_get_time() / 1000;
@@ -994,7 +1013,7 @@ void run_usb()
9941013
}
9951014
}
9961015
if (keyboard_ready && !isKeyboardPolling && (KeyboardTimer > KeyboardInterval)) {
997-
KeyboardIn->num_bytes = keyboard_bytes;
1016+
KeyboardIn->num_bytes = keyboard_bytes;
9981017
esp_err_t err = usb_host_transfer_submit(KeyboardIn);
9991018
if (err != ESP_OK) {
10001019
fprintf(stderr,"kbd usb_host_transfer_submit/In err: 0x%x\n", err);
@@ -1003,13 +1022,14 @@ void run_usb()
10031022
KeyboardTimer = 0;
10041023
}
10051024
if (mouse_ready && !isMousePolling && (MouseTimer > MouseInterval)) {
1006-
MouseIn->num_bytes = mouse_bytes;
1025+
MouseIn->num_bytes = mouse_bytes;
10071026
esp_err_t err = usb_host_transfer_submit(MouseIn);
10081027
if (err != ESP_OK) {
10091028
fprintf(stderr, "mouse usb_host_transfer_submit/In err: 0x%x\n", err);
10101029
}
10111030
isMousePolling = true;
10121031
MouseTimer = 0;
10131032
}
1033+
#endif // !USB_HOST_MIDI_ONLY
10141034
}
10151035
}

tulip/esp32s3/usb_host.h

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,17 +2,30 @@
22
#ifndef __USB_KEYBOARD_H__
33
#define __USB_KEYBOARD_H__
44

5+
// On AMYboard (AMYBOARD_USB_HOST variant) we only host USB MIDI devices —
6+
// there's no display or keyboard input path, so all the HID keyboard/mouse
7+
// handling (keyscan, lvgl, touch) is compiled out.
8+
#ifdef AMYBOARD
9+
#define USB_HOST_MIDI_ONLY 1
10+
#endif
11+
512
#include "esp_rom_gpio.h"
613
#include "driver/gpio.h"
714
#include <stdio.h>
815
#include "freertos/FreeRTOS.h"
916
#include "freertos/task.h"
1017
#include "esp_log.h"
1118
#include "usb/usb_host.h"
19+
#ifndef USB_HOST_MIDI_ONLY
1220
#include "keyscan.h"
21+
#include "display.h"
22+
#else
23+
// On Tulip this arrives via display.h -> esp32s3_display.h -> tasks.h.
24+
extern TaskHandle_t tulip_mp_handle;
25+
#endif
1326
#include "tulip_helpers.h"
1427
#include "esp_timer.h"
15-
#include "display.h"
28+
#include "amy.h" // MAX_MESSAGE_LEN (via display.h on Tulip, direct here)
1629
#include "amy_midi.h" // from extmod/tulip/
1730

1831
extern uint8_t tulip_ready;

tulip/shared/amy_connector.c

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -220,10 +220,11 @@ extern bool midi_has_out;
220220
extern void send_usb_midi_out(uint8_t * data, uint16_t len);
221221

222222
void tulip_send_midi_out(uint8_t* buf, uint16_t len) {
223-
// check if we have USB HOST midi, which is handled by Tulip only - not AMYBOARD or TDECK
223+
// check if we have USB HOST midi: Tulip, or the AMYBOARD_USB_HOST variant
224+
// — not stock AMYBOARD (USB gadget) or TDECK
224225
#ifdef ESP_PLATFORM
225226
#ifndef TDECK
226-
#ifndef AMYBOARD
227+
#if !defined(AMYBOARD) || defined(AMYBOARD_USB_HOST)
227228
if(midi_has_out) {
228229
send_usb_midi_out(buf, len);
229230
}
@@ -463,7 +464,13 @@ void run_amy(uint8_t midi_out_pin) {
463464
#endif
464465
#ifdef AMYBOARD
465466
amy_config.features.audio_in = 1;
467+
#ifdef AMYBOARD_USB_HOST
468+
// USB is a MIDI host here (usb_host.c feeds AMY directly); the TinyUSB
469+
// gadget stack is compiled but never initialized, so don't route to it.
470+
amy_config.midi = AMY_MIDI_IS_UART;
471+
#else
466472
amy_config.midi = AMY_MIDI_IS_UART | AMY_MIDI_IS_USB_GADGET;
473+
#endif
467474
#else
468475
amy_config.features.audio_in = 0;
469476
amy_config.midi = AMY_MIDI_IS_UART;

tulip/shared/modtulip.c

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -688,6 +688,11 @@ extern void run_amy(uint8_t);
688688
STATIC mp_obj_t tulip_amyboard_start(size_t n_args, const mp_obj_t *args) {
689689
uint8_t midi_out_pin = mp_obj_get_int(args[0]);
690690
run_amy(midi_out_pin);
691+
#ifdef AMYBOARD_USB_HOST
692+
// AMY is up; let the USB MIDI host task (usb_host.c) feed it MIDI.
693+
extern uint8_t tulip_ready;
694+
tulip_ready = 1;
695+
#endif
691696
return mp_const_none;
692697
}
693698
STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(tulip_amyboard_start_obj, 1, 1, tulip_amyboard_start);

0 commit comments

Comments
 (0)