Pybricks UART: MIDI IN + MIDI OUT + DMX

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

Pybricks UART: MIDI IN + MIDI OUT + DMX

Mensagem por CyberX »

Uma demo meio confusa em cima do joelho dos 3 adaptadores UART que construí:
- uart2midi
- uart2dmx
- midi2uart

os 2 primeiros já tinha mostrado em posts anteriores; o terceiro permite receber mensagens MIDI num Hub Powered Up seguindo as normas MIDI (um optoacoplador separa electricamente o equipamento emissor MIDI do circuito receptor pelo que se alguma coisa correr mal o nosso precioso equipamento não-LEGO permanece incólume)

Imagem

feito em cima do joelho mas suficientemente sólido (duas resistências, um diodo e os fios TX, GND e 3V3 do cabo Powered UP soldados directamente aos pinos do circuito integrado e uma ficha fẽmea jack estéreo de 3.5 para a ligação MIDI)

No video uso um controlador MIDI (Novation Launchpad Mini) para enviar notas para um sintetizador MIDi (Roland S-1) por intermédio do Hub Technic.

O Launchpad tem 64 teclas com iluminação RGB, neste video agrupadas em 4 'pads' de 4x4 (Red, Green, Blue, Yellow). Dos primeiros 3 pads infiro os valores RGB a enviar por DMX para o foco RGB da direita, do último infiro o valor da luminosidade a enviar por DMX ao foco UV da esquerda.

Imagem

Desta forma há uma certa correspondência entre as notas tocadas e as cores/intensidades dos projectores.

Jorge Pereira
«De génio, criança e louco... porquê só 1 pouco?»
Avatar do Utilizador
CyberX
Sócio
Sócio
Mensagens: 4535
Registado: 10 mar 2014, 20:09
Localização: Mira Sintra
Contacto:

Re: Pybricks UART: MIDI IN + MIDI OUT + DMX

Mensagem por CyberX »

Agora o código:

Código: Selecionar todos

from pybricks.hubs import TechnicHub
from pybricks.parameters import Port
from pybricks.tools import wait, StopWatch
from pybricks.iodevices import UARTDevice

hub = TechnicHub()
watch_midi = StopWatch()
watch_dmx = StopWatch()

midi_out = UARTDevice(Port.A, baudrate=31250)
midi_in = UARTDevice(Port.B, baudrate=31250)
dmx_out = UARTDevice(Port.C)

# MIDI output payload
midi_payload = bytearray(3)

# DMX output payload
dmx_payload = [b'\x00']*513 # DMX512 supports up to 512 channels
                        # preceded by a Start Code

dmx_payload[0] = b'\x00'    # Start Code

# PAR RGB has 7 channels and DMX address = 1
dmx_payload[1] = b'\x00'    # CH1 Mode
dmx_payload[2] = b'\xFF'    # CH2 RGB Color
dmx_payload[3] = b'\x00'    # Speed
dmx_payload[4] = b'\xFF'    # Master dimmer
dmx_payload[5] = b'\xFF'    # R
dmx_payload[6] = b'\x00'    # G
dmx_payload[7] = b'\x00'    # B

# PAR UV has 4 channels and DMX address = 8
dmx_payload[8] = b'\xFF'    # Master Dimmer
dmx_payload[9] = b'\xFF'    # UV Color
dmx_payload[10] = b'\x00'   # Strobe off
dmx_payload[11] = b'\x00'   # Mode

dmx_channels = 11       # no need to send more frames than the
                        # number of channels usable in the whole fixture

r = 0
g = 0
b = 0
uv = 0

watch_midi.reset()
watch_midi.resume()

watch_dmx.reset()
watch_dmx.resume()

print("\n*****\n")

while True:
    if watch_midi.time() >= 100 :
        # every 100 ms:
        #   check MIDI IN
        #   refresh DMX values (RGB and UV)

        watch_midi.reset()

        while midi_in.waiting() > 0:
            data = int.from_bytes(midi_in.read(1))
            #  print(data, end=' ')
            if data == 0xFE:
                # Active Sense, some controllers keep sending it
                #print('Activ Sense')
                pass

            elif data == 0xF8 or data == 0xFA or data == 0xFB or data == 0xFC:
                # Beat Clock / Start / Continue / Stop
                #print('Beat clock')
                pass

            #elif data & 0x90 == 0x90:
            elif data in range(0x90, 0x9F):
                # note on, read note and velocity
                note = int.from_bytes(midi_in.read(1))
                velocity = int.from_bytes(midi_in.read(1))
                channel = data & 0x0F
                if velocity != 0 :
                    print('Note ON: ', note, velocity, 'on Ch: ', channel)
                    midi_payload[0] = 144       # note on
                    midi_payload[1] = note
                    midi_payload[2] = 127       # velocity
                    midi_out.write(midi_payload)

                    if 36 <= note <= 51 :           # Yellow Pads
                        uv = (note - 36) * 17
                    elif 52 <= note <= 67 :         # Pink Pads
                        r = (note - 52) * 17
                    elif 68 <= note <= 83 :         # Blue Pads
                        b = (note - 68) * 17
                    elif 84 <= note <= 99 :         # Green Pads
                        g = (note - 84) * 17

                else:
                    print('Note OFF: ', note)
                    midi_payload[0] = 128       # note off
                    midi_payload[2] = 0         # velocity
                    midi_out.write(midi_payload)                    
            elif data in range(0x80, 0x8F):
                # note offm read note and veolocity
                note = int.from_bytes(midi_in.read(1))
                velocity = int.from_bytes(midi_in.read(1))
                channel = data & 0x0F
                print('Note OFF: ', note, velocity, 'on Ch: ', channel)
                midi_payload[0] = 128       # note off
                midi_payload[2] = 0         # velocity
                midi_out.write(midi_payload)   

    if watch_dmx.time() >= 50 :
        # every 50 ms:
        #   send DMX data to all fixtures in the universe

        watch_dmx.reset()
        print("R G B UV: ", r, g, b, uv)

        # control PARs
        dmx_payload[5] = r.to_bytes(1,'endian') # R
        dmx_payload[6] = g.to_bytes(1,'endian') # G
        dmx_payload[7] = b.to_bytes(1,'endian') # B
        dmx_payload[8] = uv.to_bytes(1,'endian') # UV Brightness

        # Break + Mark-After-Break
        # need an hack because Pybricks UART doesn't allow to define a preamble
        # so we send something similar at a lower baud rate
        dmx_out.set_baudrate(90909)
        dmx_out.write(b'\x80')

        dmx_out.set_baudrate(250000)

        for frame in range(0, dmx_channels):
            dmx_out.write(dmx_payload[frame])

os mais atentos (doidos? nerds?) vão reparar que uso 3 portas:

Código: Selecionar todos

midi_out = UARTDevice(Port.A, baudrate=31250)
midi_in = UARTDevice(Port.B, baudrate=31250)
dmx_out = UARTDevice(Port.C)
Isto porque o "adaptador" que mostrei na foto do post acima, com circuito soldado directamente ao cabo, faz apenas a conversão midi2uart.

É possível um adaptador único, bidirecional (midi2uart + uart2midi) e assim que tiver acesso a uma protoboard suficientemente pequena vou fazer um, para poder utilizar uma única porta do Hub (e um único cabo Powered Up) para simultâneamente poder enviar e receber sinais MIDI.
Jorge Pereira
«De génio, criança e louco... porquê só 1 pouco?»
Responder