Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 18 additions & 0 deletions tulip/amyboard/boards/AMYBOARD_USB_HOST/board.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
{
"deploy": [
"../deploy_s3.md"
],
"docs": "",
"features": [
"BLE",
"WiFi"
],
"images": [
"generic_s3.jpg"
],
"mcu": "esp32s3",
"product": "AMYboard (USB MIDI host)",
"thumbnail": "",
"url": "https://www.espressif.com/en/products/modules",
"vendor": "Espressif"
}
21 changes: 21 additions & 0 deletions tulip/amyboard/boards/AMYBOARD_USB_HOST/mpconfigboard.cmake
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
set(IDF_TARGET esp32s3)

set(MICROPY_PY_TINYUSB ON)


# AMYBOARD is always defined for this port (see esp32_common.cmake); this adds
# the variant macro that switches USB to host (MIDI) mode + UART REPL.
set(BOARD_DEFINITION1 AMYBOARD_USB_HOST)
set(BOARD_DEFINITION2 MAKERFABS)

set(SDKCONFIG_DEFAULTS
../../micropython/ports/esp32/boards/sdkconfig.base
../../micropython/ports/esp32/boards/sdkconfig.usb
../../micropython/ports/esp32/boards/sdkconfig.240mhz
../esp32s3/boards/sdkconfig.tulip
boards/AMYBOARD/sdkconfig.board
)

list(APPEND MICROPY_SOURCE_BOARD
../esp32s3/usb_host.c
)
15 changes: 15 additions & 0 deletions tulip/amyboard/boards/AMYBOARD_USB_HOST/mpconfigboard.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
// AMYboard variant: the USB port runs as a USB MIDI *host* (like Tulip CC)
// instead of a TinyUSB CDC + gadget-MIDI device. The REPL moves to UART0 —
// the same pins the stderr console already uses — so connect a USB-serial
// dongle to the UART header to control the board.
//
// The TinyUSB device stack is still compiled (amy_midi.c references tud_midi_*
// whenever AMYBOARD is defined) but usb_init()/tusb_init() are never called,
// so every tud_* call is a safe no-op.
#include "../AMYBOARD/mpconfigboard.h"

#undef MICROPY_HW_BOARD_NAME
#define MICROPY_HW_BOARD_NAME "AMYboard USB host"

#undef MICROPY_HW_ENABLE_UART_REPL
#define MICROPY_HW_ENABLE_UART_REPL (1)
5 changes: 5 additions & 0 deletions tulip/amyboard/boards/AMYBOARD_USB_HOST/pins.csv
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
I2C_SCL,GPIO9
I2C_SDA,GPIO8
FG_INT,GPIO21
UART0_TX,GPIO43
UART0_RX,GPIO44
21 changes: 20 additions & 1 deletion tulip/amyboard/main.c
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,15 @@ TaskHandle_t idle_1_handle;
TaskHandle_t sequencer_handle;
TaskHandle_t cv_read_handle;

#ifdef AMYBOARD_USB_HOST
// USB runs in host (MIDI) mode instead of TinyUSB device mode. usb_host.c
// gates MIDI input to AMY on tulip_ready; it's set once run_amy() has started
// AMY (see tulip_amyboard_start in modtulip.c).
TaskHandle_t usb_handle;
uint8_t tulip_ready = 0;
extern void run_usb();
#endif

// For CPU usage
unsigned long last_task_counters[MAX_TASKS];

Expand Down Expand Up @@ -201,7 +210,10 @@ void mp_task(void *pvParameter) {
#if MICROPY_PY_THREAD
mp_thread_init(pxTaskGetStackStart(NULL), MICROPY_TASK_STACK_SIZE / sizeof(uintptr_t));
#endif
#if MICROPY_HW_ESP_USB_SERIAL_JTAG
#ifdef AMYBOARD_USB_HOST
// USB is a MIDI host on this variant (started in app_main); never bring up
// the TinyUSB device stack. The REPL is on UART0 instead (with stderr).
#elif MICROPY_HW_ESP_USB_SERIAL_JTAG
usb_serial_jtag_init();
#elif MICROPY_HW_ENABLE_USBDEV
usb_init();
Expand Down Expand Up @@ -351,6 +363,13 @@ void app_main(void) {
extern void cv_read_task(void *pvParameter);
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);

#ifdef AMYBOARD_USB_HOST
fprintf(stderr,"Starting USB host on core %d\n", USB_TASK_COREID);
xTaskCreatePinnedToCore(run_usb, USB_TASK_NAME, (USB_TASK_STACK_SIZE) / sizeof(StackType_t), NULL, USB_TASK_PRIORITY, &usb_handle, USB_TASK_COREID);
fflush(stderr);
delay_ms(100);
#endif

fprintf(stderr,"Starting MicroPython on core %d\n", TULIP_MP_TASK_COREID);
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);
fflush(stderr);
Expand Down
6 changes: 6 additions & 0 deletions tulip/amyboard/tasks.h
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,12 @@
#define CV_READ_TASK_STACK_SIZE (2 * 1024)
#define CV_READ_TASK_NAME "cv_read_task"

// USB MIDI host task (AMYBOARD_USB_HOST variant only) — same shape as Tulip's.
#define USB_TASK_PRIORITY (ESP_TASK_PRIO_MIN + 1)
#define USB_TASK_COREID (1)
#define USB_TASK_STACK_SIZE (8 * 1024)
#define USB_TASK_NAME "usb_task"

#define MAX_TASKS 17 // includes system tasks

extern TaskHandle_t tulip_mp_handle;
Expand Down
56 changes: 38 additions & 18 deletions tulip/esp32s3/usb_host.c
Original file line number Diff line number Diff line change
Expand Up @@ -107,10 +107,27 @@ void show_endpoint_desc(const void *p, int indent)

#endif // DEBUG_USB

#define DEFAULT_TIMEOUT_MS (5000)

const TickType_t HOST_EVENT_TIMEOUT = 1;
const TickType_t CLIENT_EVENT_TIMEOUT = 1;

const size_t USB_HID_DESC_SIZE = 9;

usb_host_client_handle_t Client_Handle;
usb_device_handle_t Device_Handle_midi;

uint8_t Interface_Number_midi;

bool midi_claimed = false;
bool midi_has_out = false;
bool midi_has_in = false;
bool midi_ready = false;

#ifndef USB_HOST_MIDI_ONLY
#define KEYBOARD_BUFFER_SIZE 64
#define KEYBOARD_BYTES 8
#define MOUSE_BYTES 8
#define DEFAULT_TIMEOUT_MS (5000)
uint16_t keyboard_bytes = KEYBOARD_BYTES;
uint16_t mouse_bytes = MOUSE_BYTES;

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


const TickType_t HOST_EVENT_TIMEOUT = 1;
const TickType_t CLIENT_EVENT_TIMEOUT = 1;

const size_t USB_HID_DESC_SIZE = 9;

usb_host_client_handle_t Client_Handle;
usb_device_handle_t Device_Handle_kb;
usb_device_handle_t Device_Handle_midi;
usb_device_handle_t Device_Handle_mouse;

uint8_t Interface_Number_kb;
uint8_t Interface_Number_midi;
uint8_t Interface_Number_mouse;

bool keyboard_claimed = false;
bool keyboard_ready = false;

bool midi_claimed = false;
bool midi_has_out = false;
bool midi_has_in = false;
bool midi_ready = false;

bool mouse_claimed = false;
bool mouse_ready = false;

Expand All @@ -162,6 +165,7 @@ int64_t KeyRepeatTimer=0;
uint16_t current_held = 0;
int64_t current_held_ms = 0;
int64_t last_inter_trigger_ms = 0;
#endif // !USB_HOST_MIDI_ONLY


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


// This identifies a mouse HID report packet for a standard "boot" mouse.
#ifndef USB_HOST_MIDI_ONLY
// This identifies a mouse HID report packet for a standard "boot" mouse.
typedef struct {
union {
struct {
Expand All @@ -189,6 +194,7 @@ typedef struct {
int16_t mouse_x_pos = 0;
int16_t mouse_y_pos = 0;
uint8_t mouse_buttons[3];
#endif // !USB_HOST_MIDI_ONLY



Expand Down Expand Up @@ -494,6 +500,7 @@ void _client_event_callback(const usb_host_client_event_msg_t *event_msg, void *
err = usb_host_device_close(Client_Handle, Device_Handle_midi);
if (err != ESP_OK) fprintf(stderr,"usb_host_device_close err: 0x%x\n", err);
}
#ifndef USB_HOST_MIDI_ONLY
if (keyboard_claimed && (uint32_t)Device_Handle_kb == (uint32_t)event_msg->dev_gone.dev_hdl) {
err = usb_host_interface_release(Client_Handle, Device_Handle_kb, Interface_Number_kb);
if (err != ESP_OK) fprintf(stderr,"usb_host_interface_release err: 0x%x\n", err);
Expand All @@ -511,6 +518,7 @@ void _client_event_callback(const usb_host_client_event_msg_t *event_msg, void *
err = usb_host_device_close(Client_Handle, Device_Handle_mouse);
if (err != ESP_OK) fprintf(stderr,"usb_host_device_close err: 0x%x\n", err);
}
#endif // !USB_HOST_MIDI_ONLY
break;
default:
fprintf(stderr,"Unknown USB event: %d\n", event_msg->event);
Expand Down Expand Up @@ -567,6 +575,8 @@ void usbh_task(void)



#ifndef USB_HOST_MIDI_ONLY

static char lvgl_kb_buf[KEYBOARD_BUFFER_SIZE];

void lvgl_keyboard_read(lv_indev_t * indev_drv, lv_indev_data_t * data) {
Expand Down Expand Up @@ -885,6 +895,8 @@ void prepare_endpoint_hid_mouse(const void *p)
//}
}

#endif // !USB_HOST_MIDI_ONLY


void new_enumeration_config_fn(const usb_config_desc_t *config_desc, usb_device_handle_t Device_Handle) {
// We just retrieved the config of a newly-connected device.
Expand Down Expand Up @@ -922,18 +934,22 @@ void new_enumeration_config_fn(const usb_config_desc_t *config_desc, usb_device_
|| (last_descriptor == USB_B_DESCRIPTOR_TYPE_ENDPOINT)) --indent;
show_interface_desc(p, indent++);
if(!midi_claimed) { check_interface_desc_MIDI(p, Device_Handle); }
#ifndef USB_HOST_MIDI_ONLY
if(!keyboard_claimed) { check_interface_desc_boot_keyboard(p, Device_Handle); }
if(!mouse_claimed) { check_interface_desc_boot_mouse(p, Device_Handle); }
#endif
last_descriptor = bDescriptorType;
break;
case USB_B_DESCRIPTOR_TYPE_ENDPOINT:
show_endpoint_desc(p, indent);
#ifndef USB_HOST_MIDI_ONLY
if (keyboard_claimed && !keyboard_ready) {
prepare_endpoint_hid_kb(p);
}
if (mouse_claimed && !mouse_ready) {
prepare_endpoint_hid_mouse(p);
}
#endif
if (midi_claimed && !midi_ready) {
prepare_endpoint_midi(p);
}
Expand Down Expand Up @@ -972,15 +988,18 @@ void new_enumeration_config_fn(const usb_config_desc_t *config_desc, usb_device_

void run_usb()
{
#ifndef USB_HOST_MIDI_ONLY
// Reset key maps
for(uint8_t i=0;i<MAX_KEY_REMAPS;i++) {
key_remaps[i].scan = 0;
key_remaps[i].mod = 0;
key_remaps[i].code = 0;
}
#endif
usbh_setup();
while(1) {
usbh_task();
#ifndef USB_HOST_MIDI_ONLY
KeyboardTimer = esp_timer_get_time() / 1000;
MouseTimer = esp_timer_get_time() / 1000;
KeyRepeatTimer = esp_timer_get_time() / 1000;
Expand All @@ -994,7 +1013,7 @@ void run_usb()
}
}
if (keyboard_ready && !isKeyboardPolling && (KeyboardTimer > KeyboardInterval)) {
KeyboardIn->num_bytes = keyboard_bytes;
KeyboardIn->num_bytes = keyboard_bytes;
esp_err_t err = usb_host_transfer_submit(KeyboardIn);
if (err != ESP_OK) {
fprintf(stderr,"kbd usb_host_transfer_submit/In err: 0x%x\n", err);
Expand All @@ -1003,13 +1022,14 @@ void run_usb()
KeyboardTimer = 0;
}
if (mouse_ready && !isMousePolling && (MouseTimer > MouseInterval)) {
MouseIn->num_bytes = mouse_bytes;
MouseIn->num_bytes = mouse_bytes;
esp_err_t err = usb_host_transfer_submit(MouseIn);
if (err != ESP_OK) {
fprintf(stderr, "mouse usb_host_transfer_submit/In err: 0x%x\n", err);
}
isMousePolling = true;
MouseTimer = 0;
}
#endif // !USB_HOST_MIDI_ONLY
}
}
15 changes: 14 additions & 1 deletion tulip/esp32s3/usb_host.h
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,30 @@
#ifndef __USB_KEYBOARD_H__
#define __USB_KEYBOARD_H__

// On AMYboard (AMYBOARD_USB_HOST variant) we only host USB MIDI devices —
// there's no display or keyboard input path, so all the HID keyboard/mouse
// handling (keyscan, lvgl, touch) is compiled out.
#ifdef AMYBOARD
#define USB_HOST_MIDI_ONLY 1
#endif

#include "esp_rom_gpio.h"
#include "driver/gpio.h"
#include <stdio.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_log.h"
#include "usb/usb_host.h"
#ifndef USB_HOST_MIDI_ONLY
#include "keyscan.h"
#include "display.h"
#else
// On Tulip this arrives via display.h -> esp32s3_display.h -> tasks.h.
extern TaskHandle_t tulip_mp_handle;
#endif
#include "tulip_helpers.h"
#include "esp_timer.h"
#include "display.h"
#include "amy.h" // MAX_MESSAGE_LEN (via display.h on Tulip, direct here)
#include "amy_midi.h" // from extmod/tulip/

extern uint8_t tulip_ready;
Expand Down
11 changes: 9 additions & 2 deletions tulip/shared/amy_connector.c
Original file line number Diff line number Diff line change
Expand Up @@ -220,10 +220,11 @@ extern bool midi_has_out;
extern void send_usb_midi_out(uint8_t * data, uint16_t len);

void tulip_send_midi_out(uint8_t* buf, uint16_t len) {
// check if we have USB HOST midi, which is handled by Tulip only - not AMYBOARD or TDECK
// check if we have USB HOST midi: Tulip, or the AMYBOARD_USB_HOST variant
// — not stock AMYBOARD (USB gadget) or TDECK
#ifdef ESP_PLATFORM
#ifndef TDECK
#ifndef AMYBOARD
#if !defined(AMYBOARD) || defined(AMYBOARD_USB_HOST)
if(midi_has_out) {
send_usb_midi_out(buf, len);
}
Expand Down Expand Up @@ -463,7 +464,13 @@ void run_amy(uint8_t midi_out_pin) {
#endif
#ifdef AMYBOARD
amy_config.features.audio_in = 1;
#ifdef AMYBOARD_USB_HOST
// USB is a MIDI host here (usb_host.c feeds AMY directly); the TinyUSB
// gadget stack is compiled but never initialized, so don't route to it.
amy_config.midi = AMY_MIDI_IS_UART;
#else
amy_config.midi = AMY_MIDI_IS_UART | AMY_MIDI_IS_USB_GADGET;
#endif
#else
amy_config.features.audio_in = 0;
amy_config.midi = AMY_MIDI_IS_UART;
Expand Down
5 changes: 5 additions & 0 deletions tulip/shared/modtulip.c
Original file line number Diff line number Diff line change
Expand Up @@ -688,6 +688,11 @@ extern void run_amy(uint8_t);
STATIC mp_obj_t tulip_amyboard_start(size_t n_args, const mp_obj_t *args) {
uint8_t midi_out_pin = mp_obj_get_int(args[0]);
run_amy(midi_out_pin);
#ifdef AMYBOARD_USB_HOST
// AMY is up; let the USB MIDI host task (usb_host.c) feed it MIDI.
extern uint8_t tulip_ready;
tulip_ready = 1;
#endif
return mp_const_none;
}
STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(tulip_amyboard_start_obj, 1, 1, tulip_amyboard_start);
Expand Down
Loading