From 316a138ee030f6aa2f4a5e0d2afb02df1017a755 Mon Sep 17 00:00:00 2001 From: pszsh Date: Sat, 7 Mar 2026 10:43:28 -0800 Subject: [PATCH] doozy --- CMakeLists.txt | 12 +- Makefile | 5 +- btstack_config.h | 2 +- lwipopts.h | 22 ++-- main.cpp | 152 +++++++----------------- make.sh | 1 + tools/uart_bridge/CMakeLists.txt | 11 ++ tools/uart_bridge/main.c | 21 ++++ tools/uart_bridge/pico_sdk_import.cmake | 120 +++++++++++++++++++ 9 files changed, 221 insertions(+), 125 deletions(-) create mode 100755 make.sh create mode 100644 tools/uart_bridge/CMakeLists.txt create mode 100644 tools/uart_bridge/main.c create mode 100644 tools/uart_bridge/pico_sdk_import.cmake diff --git a/CMakeLists.txt b/CMakeLists.txt index e213de3..2932ef9 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -15,6 +15,7 @@ add_compile_definitions(PICO_RP2350A=0) pico_sdk_init() +set(CS_MIDI_HID_MOUSE ON CACHE BOOL "" FORCE) add_subdirectory(lib/cs-midi) # cs_midi's BTstack sources need project-level btstack_config.h and lwipopts.h @@ -22,7 +23,6 @@ target_include_directories(cs_midi PRIVATE ${CMAKE_CURRENT_LIST_DIR}) add_executable(fractional_looper main.cpp - src/encoder.cpp src/spp_midi.cpp ) @@ -31,6 +31,12 @@ target_include_directories(fractional_looper PRIVATE ${CMAKE_CURRENT_LIST_DIR}/src ) +target_compile_definitions(fractional_looper PRIVATE + PICO_STDIO_USB_STDOUT_TIMEOUT_US=0 + PICO_DEFAULT_UART_TX_PIN=12 + PICO_DEFAULT_UART_RX_PIN=13 +) + target_link_libraries(fractional_looper pico_stdlib pico_cyw43_arch_lwip_threadsafe_background @@ -42,7 +48,7 @@ target_link_libraries(fractional_looper cs_midi ) -pico_enable_stdio_usb(fractional_looper 1) -pico_enable_stdio_uart(fractional_looper 0) +pico_enable_stdio_usb(fractional_looper 0) +pico_enable_stdio_uart(fractional_looper 1) pico_add_extra_outputs(fractional_looper) diff --git a/Makefile b/Makefile index bb92bf4..175b666 100644 --- a/Makefile +++ b/Makefile @@ -6,7 +6,10 @@ all: $(BUILD_DIR)/Makefile $(BUILD_DIR)/Makefile: CMakeLists.txt @mkdir -p $(BUILD_DIR) - @cd $(BUILD_DIR) && PICO_SDK_PATH=$$HOME/Staging/pico-sdk cmake .. + @cd $(BUILD_DIR) && \ + PICO_SDK_PATH=$$HOME/Staging/pico-sdk \ + PICOTOOL_FETCH_FROM_GIT_PATH=$(CURDIR)/lib/cs-midi/tests/build/_deps \ + cmake $(CURDIR) clean: @rm -f $(BUILD_DIR)/CMakeFiles/$(TARGET).dir/main.cpp.o diff --git a/btstack_config.h b/btstack_config.h index c5280bc..6518409 100644 --- a/btstack_config.h +++ b/btstack_config.h @@ -46,7 +46,7 @@ #define NVM_NUM_DEVICE_DB_ENTRIES 16 #define NVM_NUM_LINK_KEYS 16 -#define MAX_ATT_DB_SIZE 512 +#define MAX_ATT_DB_SIZE 1024 #define HAVE_EMBEDDED_TIME_MS #define HAVE_ASSERT diff --git a/lwipopts.h b/lwipopts.h index eae4956..2e4d822 100644 --- a/lwipopts.h +++ b/lwipopts.h @@ -1,22 +1,16 @@ #ifndef _LWIPOPTS_H #define _LWIPOPTS_H +// Minimal — lwip linked by cs-midi's cyw43_arch but not used for networking #define NO_SYS 1 #define LWIP_SOCKET 0 #define LWIP_NETCONN 0 -#define LWIP_RAW 1 - -#define LWIP_DHCP 1 -#define LWIP_DNS 1 -#define LWIP_IGMP 1 -#define LWIP_NETIF_HOSTNAME 1 -#define LWIP_NETIF_STATUS_CALLBACK 1 - -#define LWIP_UDP 1 -#define LWIP_TCP 1 - -#define MEM_SIZE 4096 -#define MEMP_NUM_UDP_PCB 6 -#define MEMP_NUM_TCP_PCB 4 +#define LWIP_RAW 0 +#define LWIP_DHCP 0 +#define LWIP_DNS 0 +#define LWIP_IGMP 0 +#define LWIP_UDP 0 +#define LWIP_TCP 0 +#define MEM_SIZE 1024 #endif diff --git a/main.cpp b/main.cpp index 37e5fa3..7ce4221 100644 --- a/main.cpp +++ b/main.cpp @@ -1,131 +1,71 @@ #include #include "pico/stdlib.h" #include "pico/cyw43_arch.h" -#include "pico/btstack_flash_bank.h" -#include "btstack_tlv_flash_bank.h" -#include "hci.h" -#include "ble/le_device_db_tlv.h" -#include "classic/btstack_link_key_db_tlv.h" - #include -#include "encoder.h" #include "spp_midi.h" -// Two CC sets, toggled by SW1 button. -// Set A: encoders CC 16-19, buttons CC 20-22 (SW2-SW4) -// Set B: encoders CC 24-27, buttons CC 28-30 (SW2-SW4) -static constexpr uint8_t SET_A_ENC_CC = 16; -static constexpr uint8_t SET_A_BTN_CC = 20; -static constexpr uint8_t SET_B_ENC_CC = 24; -static constexpr uint8_t SET_B_BTN_CC = 28; -static constexpr uint8_t TOGGLE_CC = 23; +using namespace cs; -static uint8_t enc_cc_base = SET_A_ENC_CC; -static uint8_t btn_cc_base = SET_A_BTN_CC; -static bool set_b_active = false; +// SPP constructed first — its begin() registers Classic BT services before BLE starts HCI +SPPStreamMIDI_Interface spp_midi; +BluetoothMIDI_Interface ble_midi; -// LED state -enum LedState { LED_IDLE_BLINK, LED_CONNECT_FLASH, LED_OFF }; -static LedState led_state = LED_IDLE_BLINK; -static uint16_t led_counter = 0; -static uint8_t led_flash_count = 0; -static bool was_connected = false; +// State toggle on encoder 1's button — fixed CC, sends 127/0 to indicate active map +CCButtonLatched toggle_btn {1, {80, Channel_1}}; -static void led_set(bool on) { - cyw43_arch_gpio_put(CYW43_WL_GPIO_LED_PIN, on); +// Encoders (A/B swapped on hardware) +CCRotaryEncoder enc1 {{2, 0}, {16, Channel_1}, 1, 4}; +CCRotaryEncoder enc2 {{5, 3}, {17, Channel_1}, 1, 4}; +CCRotaryEncoder enc3 {{8, 6}, {18, Channel_1}, 1, 4}; +CCRotaryEncoder enc4 {{9, 11}, {19, Channel_1}, 1, 4}; + +// Remaining encoder push buttons +CCButton btn2 {4, {20, Channel_1}}; +CCButton btn3 {7, {21, Channel_1}}; +CCButton btn4 {10, {22, Channel_1}}; + +// CC mappings per state +struct CCMap { + uint8_t enc[4]; + uint8_t btn[3]; +}; + +constexpr CCMap maps[2] = { + {{16, 17, 18, 19}, {20, 21, 22}}, // state 0 + {{23, 24, 25, 26}, {27, 28, 29}}, // state 1 +}; + +void apply_map(const CCMap &m) { + enc1.setAddress({m.enc[0], Channel_1}); + enc2.setAddress({m.enc[1], Channel_1}); + enc3.setAddress({m.enc[2], Channel_1}); + enc4.setAddress({m.enc[3], Channel_1}); + btn2.setAddressUnsafe({m.btn[0], Channel_1}); + btn3.setAddressUnsafe({m.btn[1], Channel_1}); + btn4.setAddressUnsafe({m.btn[2], Channel_1}); } int main() { stdio_init_all(); - if (cyw43_arch_init()) { printf("CYW43 init failed\n"); return 1; } - printf("FractionalLooper: CYW43 initialized\n"); - // Persistent bonding storage — survives power cycles - static btstack_tlv_flash_bank_t tlv_context; - const hal_flash_bank_t *flash_bank = pico_flash_bank_instance(); - const btstack_tlv_t *tlv_impl = btstack_tlv_flash_bank_init_instance( - &tlv_context, flash_bank, NULL); - btstack_tlv_set_instance(tlv_impl, &tlv_context); - le_device_db_tlv_configure(tlv_impl, &tlv_context); - hci_set_link_key_db(btstack_link_key_db_tlv_get_instance(tlv_impl, &tlv_context)); + RelativeCCSender::setMode(relativeCCmode::MACKIE_CONTROL_RELATIVE); + ble_midi.setName("FractionalLooper"); + Control_Surface.begin(); - cs::BluetoothMIDI_Interface ble; - ble.ble_settings.initiate_security = true; - ble.begin(); - printf("BLE MIDI started\n"); - - SPPStreamMIDI_Interface spp; - spp.begin(); - - cs::BidirectionalMIDI_PipeFactory<2> pipes; - ble | pipes | spp; - - encoders_init(); - printf("Encoders initialized\n"); - - ble.setAsDefault(); + bool last_state = false; while (true) { - bool connected = ble.isConnected() || spp.isConnected(); - if (connected && !was_connected) { - led_state = LED_CONNECT_FLASH; - led_flash_count = 0; - led_counter = 0; - } else if (!connected && was_connected) { - led_state = LED_IDLE_BLINK; - led_counter = 0; + Control_Surface.loop(); + + bool state = toggle_btn.getState(); + if (state != last_state) { + last_state = state; + apply_map(maps[state]); } - was_connected = connected; - - switch (led_state) { - case LED_IDLE_BLINK: - led_set(led_counter < 500); - if (++led_counter >= 1000) led_counter = 0; - break; - case LED_CONNECT_FLASH: - led_set(led_counter < 50); - if (++led_counter >= 100) { - led_counter = 0; - if (++led_flash_count >= 6) { - led_state = LED_OFF; - led_set(false); - } - } - break; - case LED_OFF: - break; - } - - for (int i = 0; i < NUM_ENCODERS; i++) { - int32_t delta = encoder_get_delta(i); - if (delta != 0) { - int32_t clamped = delta; - if (clamped > 63) clamped = 63; - if (clamped < -63) clamped = -63; - uint8_t val = (clamped > 0) ? (uint8_t)clamped - : (uint8_t)(128 + clamped); - ble.sendControlChange({enc_cc_base + i, cs::Channel_1}, val); - } - - if (button_pressed(i)) { - if (i == 0) { - set_b_active = !set_b_active; - enc_cc_base = set_b_active ? SET_B_ENC_CC : SET_A_ENC_CC; - btn_cc_base = set_b_active ? SET_B_BTN_CC : SET_A_BTN_CC; - ble.sendControlChange({TOGGLE_CC, cs::Channel_1}, - set_b_active ? 127 : 0); - } else { - ble.sendControlChange({btn_cc_base + (i - 1), cs::Channel_1}, 127); - } - } - } - - ble.update(); - spp.update(); sleep_ms(1); } diff --git a/make.sh b/make.sh new file mode 100755 index 0000000..dd88204 --- /dev/null +++ b/make.sh @@ -0,0 +1 @@ +make clean && make && make flash \ No newline at end of file diff --git a/tools/uart_bridge/CMakeLists.txt b/tools/uart_bridge/CMakeLists.txt new file mode 100644 index 0000000..7df8e0b --- /dev/null +++ b/tools/uart_bridge/CMakeLists.txt @@ -0,0 +1,11 @@ +cmake_minimum_required(VERSION 3.13) +include(pico_sdk_import.cmake) +project(uart_bridge C CXX ASM) +set(CMAKE_C_STANDARD 11) +pico_sdk_init() + +add_executable(uart_bridge main.c) +target_link_libraries(uart_bridge pico_stdlib) +pico_enable_stdio_usb(uart_bridge 1) +pico_enable_stdio_uart(uart_bridge 0) +pico_add_extra_outputs(uart_bridge) diff --git a/tools/uart_bridge/main.c b/tools/uart_bridge/main.c new file mode 100644 index 0000000..deee118 --- /dev/null +++ b/tools/uart_bridge/main.c @@ -0,0 +1,21 @@ +// UART-to-USB CDC bridge for Pico +// UART0 RX on GPIO 1, 115200 baud → USB serial output +#include "pico/stdlib.h" +#include + +#define UART_ID uart0 +#define UART_RX_PIN 1 +#define BAUD 115200 + +int main() { + stdio_init_all(); + uart_init(UART_ID, BAUD); + gpio_set_function(UART_RX_PIN, GPIO_FUNC_UART); + + while (true) { + if (uart_is_readable(UART_ID)) { + char c = uart_getc(UART_ID); + putchar(c); + } + } +} diff --git a/tools/uart_bridge/pico_sdk_import.cmake b/tools/uart_bridge/pico_sdk_import.cmake new file mode 100644 index 0000000..e215d32 --- /dev/null +++ b/tools/uart_bridge/pico_sdk_import.cmake @@ -0,0 +1,120 @@ +# This is a copy of /external/pico_sdk_import.cmake + +# This can be dropped into an external project to help locate this SDK +# It should be include()ed prior to project() + +# Copyright 2020 (c) 2020 Raspberry Pi (Trading) Ltd. +# +# Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +# following conditions are met: +# +# 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +# disclaimer. +# +# 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following +# disclaimer in the documentation and/or other materials provided with the distribution. +# +# 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products +# derived from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +# INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +# WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +# THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +if (DEFINED ENV{PICO_SDK_PATH} AND (NOT PICO_SDK_PATH)) + set(PICO_SDK_PATH $ENV{PICO_SDK_PATH}) + message("Using PICO_SDK_PATH from environment ('${PICO_SDK_PATH}')") +endif () + +if (DEFINED ENV{PICO_SDK_FETCH_FROM_GIT} AND (NOT PICO_SDK_FETCH_FROM_GIT)) + set(PICO_SDK_FETCH_FROM_GIT $ENV{PICO_SDK_FETCH_FROM_GIT}) + message("Using PICO_SDK_FETCH_FROM_GIT from environment ('${PICO_SDK_FETCH_FROM_GIT}')") +endif () + +if (DEFINED ENV{PICO_SDK_FETCH_FROM_GIT_PATH} AND (NOT PICO_SDK_FETCH_FROM_GIT_PATH)) + set(PICO_SDK_FETCH_FROM_GIT_PATH $ENV{PICO_SDK_FETCH_FROM_GIT_PATH}) + message("Using PICO_SDK_FETCH_FROM_GIT_PATH from environment ('${PICO_SDK_FETCH_FROM_GIT_PATH}')") +endif () + +if (DEFINED ENV{PICO_SDK_FETCH_FROM_GIT_TAG} AND (NOT PICO_SDK_FETCH_FROM_GIT_TAG)) + set(PICO_SDK_FETCH_FROM_GIT_TAG $ENV{PICO_SDK_FETCH_FROM_GIT_TAG}) + message("Using PICO_SDK_FETCH_FROM_GIT_TAG from environment ('${PICO_SDK_FETCH_FROM_GIT_TAG}')") +endif () + +if (PICO_SDK_FETCH_FROM_GIT AND NOT PICO_SDK_FETCH_FROM_GIT_TAG) + set(PICO_SDK_FETCH_FROM_GIT_TAG "master") + message("Using master as default value for PICO_SDK_FETCH_FROM_GIT_TAG") +endif() + +set(PICO_SDK_PATH "${PICO_SDK_PATH}" CACHE PATH "Path to the Raspberry Pi Pico SDK") +set(PICO_SDK_FETCH_FROM_GIT "${PICO_SDK_FETCH_FROM_GIT}" CACHE BOOL "Set to ON to fetch copy of SDK from git if not otherwise locatable") +set(PICO_SDK_FETCH_FROM_GIT_PATH "${PICO_SDK_FETCH_FROM_GIT_PATH}" CACHE FILEPATH "location to download SDK") +set(PICO_SDK_FETCH_FROM_GIT_TAG "${PICO_SDK_FETCH_FROM_GIT_TAG}" CACHE FILEPATH "release tag for SDK") + +if (NOT PICO_SDK_PATH) + if (PICO_SDK_FETCH_FROM_GIT) + include(FetchContent) + set(FETCHCONTENT_BASE_DIR_SAVE ${FETCHCONTENT_BASE_DIR}) + if (PICO_SDK_FETCH_FROM_GIT_PATH) + get_filename_component(FETCHCONTENT_BASE_DIR "${PICO_SDK_FETCH_FROM_GIT_PATH}" REALPATH BASE_DIR "${CMAKE_SOURCE_DIR}") + endif () + FetchContent_Declare( + pico_sdk + GIT_REPOSITORY https://github.com/raspberrypi/pico-sdk + GIT_TAG ${PICO_SDK_FETCH_FROM_GIT_TAG} + ) + + if (NOT pico_sdk) + message("Downloading Raspberry Pi Pico SDK") + if (${CMAKE_VERSION} VERSION_GREATER_EQUAL "3.17.0") + FetchContent_Populate( + pico_sdk + QUIET + GIT_REPOSITORY https://github.com/raspberrypi/pico-sdk + GIT_TAG ${PICO_SDK_FETCH_FROM_GIT_TAG} + GIT_SUBMODULES_RECURSE FALSE + + SOURCE_DIR ${FETCHCONTENT_BASE_DIR}/pico_sdk-src + BINARY_DIR ${FETCHCONTENT_BASE_DIR}/pico_sdk-build + SUBBUILD_DIR ${FETCHCONTENT_BASE_DIR}/pico_sdk-subbuild + ) + else () + FetchContent_Populate( + pico_sdk + QUIET + GIT_REPOSITORY https://github.com/raspberrypi/pico-sdk + GIT_TAG ${PICO_SDK_FETCH_FROM_GIT_TAG} + + SOURCE_DIR ${FETCHCONTENT_BASE_DIR}/pico_sdk-src + BINARY_DIR ${FETCHCONTENT_BASE_DIR}/pico_sdk-build + SUBBUILD_DIR ${FETCHCONTENT_BASE_DIR}/pico_sdk-subbuild + ) + endif () + + set(PICO_SDK_PATH ${pico_sdk_SOURCE_DIR}) + endif () + set(FETCHCONTENT_BASE_DIR ${FETCHCONTENT_BASE_DIR_SAVE}) + else () + message(FATAL_ERROR + "SDK location was not specified. Please set PICO_SDK_PATH or set PICO_SDK_FETCH_FROM_GIT to on to fetch from git." + ) + endif () +endif () + +get_filename_component(PICO_SDK_PATH "${PICO_SDK_PATH}" REALPATH BASE_DIR "${CMAKE_BINARY_DIR}") +if (NOT EXISTS ${PICO_SDK_PATH}) + message(FATAL_ERROR "Directory '${PICO_SDK_PATH}' not found") +endif () + +set(PICO_SDK_INIT_CMAKE_FILE ${PICO_SDK_PATH}/pico_sdk_init.cmake) +if (NOT EXISTS ${PICO_SDK_INIT_CMAKE_FILE}) + message(FATAL_ERROR "Directory '${PICO_SDK_PATH}' does not appear to contain the Raspberry Pi Pico SDK") +endif () + +set(PICO_SDK_PATH ${PICO_SDK_PATH} CACHE PATH "Path to the Raspberry Pi Pico SDK" FORCE) + +include(${PICO_SDK_INIT_CMAKE_FILE})