midi2pu - um conversor MIDI para LEGO

Para os mais especializados em mecânica e electrónica em LEGO.
Responder
Avatar do Utilizador
CyberX
Sócio
Sócio
Mensagens: 4399
Registado: 10 mar 2014, 20:09
Localização: Mira Sintra
Contacto:

midi2pu - um conversor MIDI para LEGO

Mensagem por CyberX »

Extendi o meu DIY sensor compatível com LEGO Powered Up
com uma outra library para Arduino que permite usar MIDI, um protocolo usado para interligação de instrumentos e outros equipamentos musicais.

O MIDI originalmente utilizava fichas DIN de 5 pinos, ainda existe muito equipamento com pelo menos uma dessas fichas (IN ou OUT, por vezes também TRU para permitir daisy chaining) mas depois foi extendida para o USB MIDI (e mais recentemente para Bluetooth embora com bastante menos sucesso, vá-se lá perceber porquê >:D )

Como os microcontroladores que tenho estado a usar têm todos USB é muito fácil criar um dispositivo USB MIDI apenas com software (uma library chamama Control_Surface). Também seria possível DIN mas teria de soldar alguns componentes adicionais, assim é muito mais simples, a única diferença é que não é possível ligar directamente dois dispositivos USB MIDI sem pelo meio haver um "USB MIDI host" que faça o routeamento das mensagens MIDI. Nada que não haja cá em casa.

Com um Raspberry Pi Pico 2, um cabo vindo da Ásia e uma caixa pequena temos um dispositivo relativamente discreto:

Imagem

Imagem

Resumo da implementação:
- com o Control_Surface o meu microcontrolador apresenta-se como um dispositivo MIDI e consigo "escutar" mensagens como por exemplo as notas enviadas por um teclado MIDI
- com a minha variante do MyOwnBricks consigo enviar dados para o Hub Technic
- com mais meia duzia de linhas de C++ junto as duas libraries e reencaminho as notas MIDI recebidas por USB para a ficha PU
- no Pybricks aciono cada um de 3 motores conforme as notas recebidas



Vantagens:
- não há Bluetooth pelo meio logo não há latência
- é possível ter mais micontroladores em paralelo, cada um ligado a um Hub, todos eles pendurados num único HUB USB

Desvantagens:
- como o Technic Hub só tem 4 portas Powered Up, gastando uma com o conversor só posso controlar 3 motores; se quiser usar isto com a gaita de foles que tem 8 "dedos" preciso de 3 Technic Hubs
- não há latência mas também não há milagres, ainda tenho de estudar isto do Control_Surface para evitar algumas situações que ocorrem quando estou a tocar mais que uma nota ao mesmo tempo e páro demasiado rápido
Jorge Pereira
«De génio, criança e louco... porquê só 1 pouco?»
Avatar do Utilizador
CyberX
Sócio
Sócio
Mensagens: 4399
Registado: 10 mar 2014, 20:09
Localização: Mira Sintra
Contacto:

Re: midi2pu - um conversor MIDI para LEGO

Mensagem por CyberX »

"tenho de estudar isto do Control_Surface para evitar algumas situações que ocorrem quando estou a tocar mais que uma nota ao mesmo tempo e páro demasiado rápido"
isto já apanhei, tem mais a ver com a library que implementa Powered Up do que a library MIDI:

por qualquer motivo não dá vazão a mais que dez transmissões por segundo para o HUB LEGO; penso que esta limitação venha de alguma opção menos eficiente no código da library já que o microcontrolador executa cerca de 200 milhões de instruções por segundo e o protocolo LEGO PU comunica a 115200 bps, mesmo com o overhead do protocolo daria perfeitamente para 1000 transmissões por segundo;

além disso, por opção minha, apenas estou a enviar um byte a cada transmissão (estou a reaproveitar código anterior, até aqui limitei-me a emular um dispositivo capaz de enviar ou receber 1 byte apenas mas é possível mais)

quando faço um acorde de 2 notas no teclado midi, são gerados 2 eventos de "note on" e quando largo simutâneamente as 2 teclas são gerados 2 eventos de "note off". Da maneira que estava a processar os eventos, apenas um era enviado a cada ciclo de transmissão e por isso por vezes o acorde apenas activava um motor e por vezes o final do acorde não desactivava todos os motores (e das vezes que funcionava era porque na verdade havia uma pequena diferença de tempo entre os meus dedos e isso permitia o envio dos eventos em separado)

Implementei uma queue FIFO do lado da recepção MIDI e a cada 100 ms tiro o primeiro evento e envio.

Assim já funciona bem mas assim num acorde de 2 notas uma delas chega ao hub 100 ms depois da outra e num acorde de 3 notas uma delas chega 100 ms e outra chega 200 ms o que já começa a ser mau. Vou ter de implementar a possibilidade de enviar 3 bytes a cada transmissão para poder dar resposta a acordes de até 3 notas.

E enquanto não descobrir a razão da limitação a 10 transmissões por segundo fico-me com um lag de 100 ms na resposta a eventos MIDI e um limite a 5 notas por segundo (5 eventos 'note on' e 5 eventos 'note off') o que não me preocupa muito, numa música moderna não-trash metal o ritmo anda em torno das 2 notas por segundo.

Quando for implementar outros conversores (tenho pelo menos a ideia de um DMX2PU para poder integrar uma máquina de bolhas de sabão em LEGO num sistema DMX de controlo de palco) terei de ter em conta estas limitações.
Jorge Pereira
«De génio, criança e louco... porquê só 1 pouco?»
Avatar do Utilizador
CyberX
Sócio
Sócio
Mensagens: 4399
Registado: 10 mar 2014, 20:09
Localização: Mira Sintra
Contacto:

Re: midi2pu - um conversor MIDI para LEGO

Mensagem por CyberX »

CyberX Escreveu: 26 abr 2025, 09:24 por qualquer motivo não dá vazão a mais que dez transmissões por segundo para o HUB LEGO; penso que esta limitação venha de alguma opção menos eficiente no código da library já que o microcontrolador executa cerca de 200 milhões de instruções por segundo e o protocolo LEGO PU comunica a 115200 bps, mesmo com o overhead do protocolo daria perfeitamente para 1000 transmissões por segundo;
Ao fim de quase duas semanas a partir pedra lá descobri a razão das 10 transmissões por segundo: os exemplos do MyOwnBricks apenas invocam um comando ("process") que se limita a responder ao HUB apenas quando este lhe pergunta "estás aí?", o que acontece exactamente de 100 em 100 ms (10 vezes por segundo portanto).

Lá descobri que posso invocar outro comando para forçar o microcontrolador a enviar para o HUB. Estou a fazê-lo agora cada 10 ms (portanto 100x por segundo) e funciona bastante bem, podia até puxar mais por ele mas para MIDI está bem assim, consigo reencaminhar streams de 300 bpm (beats por minuto) sem problema:



Uma música em geral anda entre os 90 e os 120 bpm, apenas coisas como o Death Metal chegam aos 300 bpm e não estou a pensar ir por aí :D

No video uso um sequencidor MIDI que envia um evento a cada batimento. Como uma nota tem dois eventos ("note on" seguido de "note off") e o meu código no HUB apenas se limita a fazer o motor rodar com note on e pará-lo com note off, quando o ritmo é muio alto o motor mal tem tempo para arrancar quando já o estão a mandar parar.

Por isso agrupei alguns batimentos para alongar os eventos: os primeiros 4 accionam o primeiro motor, os 2 últimos o terceiro motor.

Uma alternativa seria do lado do HUB implementar código para alongar a duração de cada motor mas por enquanto não quero ir por aí, quero manter isto generalista o suficiente.
Jorge Pereira
«De génio, criança e louco... porquê só 1 pouco?»
Responder