Learning with mistakes

This post is part 2 of 5 of  LEGO bagpiper

A little progress has been made. And a lot have been learnt.

So my fingertips worked… sort of. But the ‘microphones’ were not large enough to close the holes. And rigid plastic is not good, rubber or something else soft is better to cover the holes. I am now using the 1×1 rubber pads used on LEGO Technic tracks (like in the BOOST set):

part 2437

Still not perfect but better.

Also found out that rubber balloons have some non-linear properties. After reaching some internal pressure level, if I keep inflating it the pressure drops drastically. So my idea of using a pressure sensor to regulate air volume is not so easy to achieve. Elastic hysteresis doesn’t help either…

I increased the LEGO air compressor air flow by changing the 2x Power Function ‘L’ motors with ‘XL’ motors and gearing it faster. But still not enough for long tests and still too noisy. So for now I am using a small air pump used on Arduino and other projects. At 5V it’s almost strong enough to ditch the “bag” (the balloon).

This is now the current state of the project:

Tuxie McPython – alfa version
  1. LEGO MINDSTORMS EV3
  2. Wi-Fi Power Plug
  3. Mini air pump
  4. Mindsensors pressure sensor for EV3
  5. Party Balloon
  6. MINDSTORMS EV3 medium motor
  7. LEGO custom air valve
  8. LEGO Powered Up Technic Hub (2x)
  9. Practice chanter
  10. LEGO Powered Up Large Angular motor (8x)

The air pump is powered by a power wall adapter connected to a Wi-Fi Power Plug. The plug state (ON/OFF) is controlled through UDP messages sent from the EV3.

The EV3 also reads the pressure inside the pneumatic circuit with a Mindsensore pressure sensor and tries to keep pressure steady by turning the air pump ON whenever pressure drops bellow a threshold.

The air valve is used only at start, when we need to inflate the balloon. The EV3 medium motor is used to close the valve (squeezing the aquarium air tube between the balloon and the chanter) and when there is enough air in the balloon the valve is opened.

Each Technic Hub is responsible for controlling 4 LEGO fingers with 4 Powered Up Large Angular motors. Probably smaller and faster motors would be better since they have much more strength than needed (if I don’t take care, they bend the “fingers” easily)… but that’s what I have with internal zero reference, it helps coding (but I did I had a problem with two of them, looks like they came with a large offset, near 100º… had to replace them).

The whole setup can now be described as an IoT project. I don’t have everything integrated yet so the EV3 functions are still independent of the Technic Hub functions:

  • the EV3 can be controlled manually and/or with MQTT messages
  • the Technic Hub can be controlled with Jupyter Notebook (using pybricksdev) or with two Chrome browsers running the Pybricks IDE

I have a video of current stage in action – please be warned, terrible noise:

In this video the reed has an elastic ring applied to it (I ordered a bag with the chanter). It does reduce the noise a bit but it also changes the required air pressure to play it so I ended up with a very large balloon… and that also changes the reed’s sound. Will have to make further tests to choose the best balloon size and try to adjust my code for it.

In the meantime, wainting for two orders to arrive:

  • some other practice reeeds to check if that makes any difference in the sound (I feel that my reed sounds like a cracked cane… my coding is bad, my setup is not the best but it does sound odd)
  • a pair of USB adapters for the Technic Hubs, I am tired of replacing batteries

Tuxie McPython – a LEGO bagpiper (or at least a noise machine)

This post is part 1 of 5 of  LEGO bagpiper

So confination has been tough. One year at home, one year of remodeling and repairing on my building – a 9 floor building with 36 apartments and it seems many neighbors saw the major repairs as an opportunity to make their own remodeling.

So I almost went crazy (or maybe I already am) and found myself thinking on playing bagpipes on the roof, scaring the falcons that appear at spring near my village (one even made a nest on this building, some years ago). And of course, retaliating to all the noise.

Of course, I don’t know how to play bagpipes (or any other instrument). I don’t even own a bagpipe – just a toy bagpipe that we bought on Scotland on our honeymoon:

It’s not a proper bagpipe, the drones are false and the bag looses air but the chanter (the long tube at right with a reed inside that makes a hell of a noise when air passes through it) works and is also detachable so I use it as a sort of practice chanter (the reed inside is also replaceable).

Almost 2 years ago I already tried to fill this bagpipes with my brute-force LEGO air compressor:

but it needs a lot of air and it also needs somone to squeeze the bag. I am too lazy for that.

But I read a lot. And found people making their own bagpipes with plastic bags, rubber or latex gloves and even balloons… like these dudes that use a melodica and 2 human slaves to pump air into the balloon:

So I decided to try my own robotic LEGO bagpiper proof of concept. I just needed enough air, a balloon and a chanter so I ordered a practice chanter (more affordable than an usual bagpipes chanter and a bit easier to play because usally it requires less air pressure).

And the first attempt looked promising:

and then all the usual problems on a proof of concept project started 🙂

First thing I found was that LEGO pneumatic tubes aren’t large enough to supply enough air fast enough for the reed inside the chanter to play. So the last tube, connecting the balloon to the chanter’s mouth piece, had to be replaced by aquarium tube (1 or 2 mm wider than LEGO’s).

The same applies to LEGO pneumatic switches (valves). If I want to control the flow I need a custom valve. I can squeeze the tube or I can bend it (like Nico71 does on his “Lego Pneumatic Switchless Engine – New Valve Design“).

I also needed a larger balloon – softer than the first I used so less pressure needed from the air compressor side.

And finally a needed a better air compressor. 4 MINDSTORMS motors acting over 8 large Technic pneumatic pumps wasn’t enough (and also makes a lot of noise – I want most noise to be from the bagpipes).

Luckily I already had a LPEpower SYS Inline 3 engine I used on my last LUG event. It’s an air engine – you inject compressed air and get mechanic rotation – but it can be reverse used (probably with less efficiency).

So my first attempt evolved to this:

The SYS Inline 3 engine was using a LEGO Power Functions L motor but I felt that I could obtain better results if I applied more power… so I added a second motor and used gears to combine the power (and also rotate it a bit more faster). That resulted in almost a minute of “playing”, not bad. But also showed up a new problem: too much air pressure on the reed makes it block, not generating any sound and also making the balloon accumulate even more air. For now, as I am not yet controllong the flow, my only option is manually reducing the air flow from the air compressor.

With a powerful compressor I added the first “fingers” using stick tack to close the chanter holes:

Not good but it was a start.

So I searched my LEGO spare parts after something that could close the hole and I found 2 similar parts (images are from Bricklink site):

LEGO minifig microphone (90370)
LEGO Technic pin with towball (6628)

The microphone is a bit smaller than the towball so I used it for the smaller holes on the chanter (the higher pitch notes). Also used a motor to controlling the finger for the first time – so finally we have some Python to explain McPython’s name:

It seemed OK so I added the rest of the fingers (7 fingers upside, 1 thumb downside).
With 8 Large Powered Up motors (from Volvo set or SPIKE / Robotic Inventor sets) and 2 Control+ hubs running Pybricks Tuxie McPython has reached Proof of Concept level:

So for Alpha Release level this is the roadmap:

  • coordinate the fingers – that requires me to control the 2 Control+ hubs so I will use Jupyter Notebook
  • control the air flow – that requires a custom valve to squeeze the aquarium tube, a pressure sensor to check the balloon’s current state and some sort of relay to turn the compressor power ON or OFF (so a MINDSTORMS EV3 will join soon, explaining the Tuxie name)
  • learn basic bagpipes music – by far the toughest part (but my wife already started helping me converting music scores to notes and temporizations)

Not sure if I deal with it now but also a stronger air compressor will be needed on the long run that allows Tuxie McPython to play for at least 2 minutes (time enough to drive everyone crazy before the police arrives)

LEGO DMX Fire Machine

This post is part 6 of 6 of  LEGO and DMX

Second LEGO DMX fixture: a Fire Machine 🙂
Similar script on EV3, listening to DMX channel 1 and reacting to just two values:

  • 255 turns the BIC lighter ON
  • 0 turns it OFF again

Working quite good with QLC+ as a DMX controller (at 9 Hz DMX frame rate) and other DMX fixtures on the loop (in this video a Fonestar PAR-18L set to DMX channel 2):

‘DMXfire.py’ script:

#!/usr/bin/env python3

from ev3dev2.motor import MediumMotor, OUTPUT_A, SpeedPercent
from time import sleep
import pyftdi.serialext

port = pyftdi.serialext.serial_for_url('ftdi://ftdi:232:AG0KAEQB/1', baudrate=250000)

#works good with QLC+ at 9 Hz
port.timeout = 0.0226

# clear input buffer (not sure if everything needed)
port.reset_input_buffer()
port.read(514)
port.reset_input_buffer()

channels = [0]*512
CH_FIRE = 1

fire = MediumMotor(OUTPUT_A)
fire.reset()
fire.position_sp = 0
fire.speed_sp = 1560

fireON = False

while True:
    port.reset_input_buffer()
    dmx_data = port.read(514)

    i = 0
    error = False
    for b in dmx_data:
        if i == 0 or i == 1:
            if b != 0:
                error = True
        else:
            channels[i-2] = b
        i = i + 1
    if error:
        # first 2 bytes should be zero ("start-of-packet procedure" and "start frame") 
        print("Error!")
    else:   
        if channels[CH_FIRE -1] == 255:
            if fireON == False:
                # turn Fire ON
                print('Fire triggered ON')
                fireON = True
                fire.position_sp = 3050
                fire.run_to_abs_pos()
        elif channels[CH_FIRE -1] == 0:
            if fireON == True:
            # turn Fire OFF 
            print('Fire triggered OFF')
            fireON = False 
            fire.position_sp = 0
            fire.run_to_abs_pos() 
    print("FireON: ", fireON)
    sleep(0.1)

DMX – a few findings

This post is part 5 of 6 of  LEGO and DMX

Installed QLC+ on my laptop. Great tool, giving up on bying a DMX controller.

QLC+ recognizes my FTDI cable adapter (and my Waveshare adapter):

Plugin   Device                            Input   Output  Feedback
 DMX USB  1:USB-RS485 Cable (S/N:FT4NMHF6)  -       X       -
 DMX USB
 This plugin provides DMX output support for DMXKing ultraDMX range, Enttec DMX USB Pro, Enttec Open DMX USB, FTDI USB COM485 Plus1, Vince USB-DMX512 and compatible devices.

No input support available.
1: USB-RS485 Cable (S/N: FT4NMHF6)
Device is operating correctly.
Driver in use: libFTDI
Protocol: Open DMX USB
Manufacturer: FTDI
DMX Channels: 512
DMX Frame Frequency: 30Hz
System Timer Accuracy: Patch this widget to a universe to find out

In the plugin settings I can change Mode. Open TX (above) allows sending DMX messages, Open RX allows receiving. And it works.

Using the two USB adapters – one on my laptop and the other on EV3 – I used QLC+ to test my scripts on EV3:

  • using QLC+ as controller, my EV3 python scripts (pyftdi-based) can decode messages on any of the 512 channels… as long as I lower DMX frame frequency to 9 Hz.
  • using the Ev3 as controller (python scripts using dmx485) I can receive messages on all 512 channels on QLC+

but…

  • using laptop as a controller (python dxm485 instead of QLC+) the same pyftdi-based scripts on EV3 no longer work… unless I force sender object to stop immediately after the first frame:
sender.start()
 while True:
    sender.start()
    sender.set_data(bytes(CMD))
    sleep(0.115)
    sender.stop()
    sleep(1.0)

It seems dmx485 forces a DMX Frame Frequency too high for my decoding scripts to keep up – it seems to be a limitation of EV3 because reverting the scenario (EV3 as a controller with dmx485-based script and laptop as a receiver with pyftdi-based script) it works.

Good that QLC+ has an option to adjust it – 9 Hz isn’t good for light animations but is more than enough for what I want. Will use it as a DMX controller from now on.

Interesting, QLC+ also has a MIDI plugin. It can listen to MIDI channels and take actions… like activating a DMX fixture when a selected note is played.

So… MIDI brings me to DMX… DMX brings me back to MIDI. Such a small world.

Compatibility issues

This post is part 4 of 6 of  LEGO and DMX

So my second DMX fixture arrived today: a Fonestar PAR-18L.

I had ordered a few weeks ago with a cheap Waveshare USB-RS485 adapter but the order took ages so last week I went for the Ibiza PAR-MINI-RGB-3 and the FTDI USB-RS485-WE-1800-BT cable.

So:

  • My adapter based on the FTDI cable can control both PARs
  • My cheap Cameo Control 6 controller can only control the Ibiza PAR
  • An adapter based on the Waveshare adapter cannot control the Ibiza PAR (sometimes I get a short flash of the intended color)

So my Cameo DMX controller was a bad starter choice, I will need a better controller to confirm my receiver code is working correctly. Will try to get a better one next month, with a few more channels and (if possible) some MIDI integration.

–edited–

Now even more strange: the Waveshare adapter does work… but only with the new Fonestar PAR.

I changed the Ibiza address to 008 and kept Fonestar at 001 the joined both on the DMX chain and changed my python script to use both. With the FTDI cable both PARs work. With the Waveshare only the Fonestar work, no matter if I put it as the first or the last of the chain so it doesn’t seem to be a termination problem.

As the Cameo controller works with the Ibiza but not with the Fonestar AND the Cameo controller data can only be read by my scripts when I cute B (DMX-) wire… I suspect both the Cameo and the Ibiza have A/B (DMX+/DMX-) reversed or even stranger deviations from the DMX standard.

My first LEGO DMX fixture

This post is part 3 of 6 of  LEGO and DMX

probably also first LEGO DMX fixture ever in the multiverse 🙂

So my Cameo Control 6 DMX controller is not probably full DMX compliant – I can only read 6 channels and even so only when cutting one of the data wires off. But who cares? (I do, but this month’s budget is already exceeded so I will only get a more decent DMX controller for Easter).

But using PyFtdi on EV3 works:

pip3 install ftdi

I could not confirm my FTDI url with ‘ftdi_urls.py’ like I did on my laptop but used same url (‘ftdi://ftdi:232:FT4NMHF6/1’) and it worked:

#!/usr/bin/env python3

from ev3dev2.motor import MediumMotor, LargeMotor, DcMotor, OUTPUT_A, OUTPUT_B, OUTPUT_C,  SpeedPercent
from ev3dev2.port import LegoPort
from time import sleep

lights_port = LegoPort(OUTPUT_C)
lights_port.mode = 'dc-motor'

sleep(1.5)

import pyftdi.serialext
port = pyftdi.serialext.serial_for_url('ftdi://ftdi:232:FT4NMHF6/1', baudrate=250000)
port.timeout=0

port.reset_input_buffer()
port.read(8)

channels = [0]*6

pan = MediumMotor(OUTPUT_A)
pan.position = 0
pan.speed_sp = 90

tilt = LargeMotor(OUTPUT_B)
tilt.position = 0
tilt.speed_sp = 90

lights = DcMotor(OUTPUT_C)
lights.run_direct()
lights.duty_cycle_sp = 0

while True:
    port.reset_input_buffer()
    dmx_data = port.read(8)
    sleep(0.01)
    port.reset_input_buffer()
    port.read(8)

    print(dmx_data)
    i = 0
    error = False
    for b in dmx_data:
    #    print(b)
        if i == 0 or i == 1:
            if b != 0:
                error = True
        else:
            channels[i-2] = b
        i = i + 1
    if error:
        print("Error!")
    else:
#        print("Channels:", channels)
        pan.on_to_position(SpeedPercent(25), channels[0] * 90/255 - 45)
        tilt.on_to_position(SpeedPercent(25), 45 - channels[1] * 90/255)
        lights.duty_cycle_sp = channels[2] * (100/255)


Now it’s time to plan a few DMX fixtures: a bubble machine, a fog machine, a spark machine, a confetti cannon, a flamethrower… 😀

DMX slave?

This post is part 2 of 6 of  LEGO and DMX

Controlling DMX fixtures with EV3 is cool but what I really want is creating my own LEGO fixtures – like the bubble maker.

The USB DMX adapter is great for generating DMX messages but I was in doubt if it could be also used to receive DMX messages since most DMX interfaces that allow TX/RX make use of some kind of microcontroller to reduce overhead.

Nevertheless I decided to give it a try. The cable uses a FTDI FT232R chip that is supported by PyFtdi for speeds up to 3 Mbps and the DMX “only” requires 250 kbps.

First attempts on my laptop with the very cheap 6-channel controller I have look promising:

import pyftdi.serialext
port = pyftdi.serialext.serial_for_url('ftdi://ftdi:232:FT4NMHF6/1', baudrate=2500000)
port.timeout=0.002
port.reset_input_buffer()
port.read(8)

With channels 1, 3 and 5 at maximum and channels 2, 4 and 6 at minimum I get:

b'\x00\x00\xff\x00\xff\x00\xff\x00'

and with the opposite (channels 1, 3 and 5 at minum and channels 2, 4 and 6 at maximum) I get:

b'\x00\x00\x00\xff\x00\xff\x00\xff'

The second byte always being ‘\x00’ is expected – the first frame or Start Code is ‘0’ for most common DMX usages.

And the first byte is the practical result of “the start-of-packet procedure is a logic zero for more than 22 bit periods,followed by a logic 1 for more than 2 bit periods“.

Now the problem is that if I read more than 8 bytes I will get a the same results repeated – for instance if I read 514 bytes (start-of-packet + start code + 512 channels) with all 6 channels at maximum I will get:

b'\x00\x00\xff\xff\xff\xff\xff\xff\x00\x00\xff\xff\xff\xff\xff\xff\x00\x00\xff\xff\xff\xff\xff\xff\x00\x00\xff\xff\xff\xff\xff\xff\x00\x00\xff\xff\xff\xff\xff\xff\x00\x00\xff\xff\xff\xff\xff\xff\x00\x00\xff\xff\xff\xff\xff\xff\x00\x00\xff\xff\xff\xff\xff\xff\x00\x00\xff\xff\xff\xff\xff\xff\x00\x00\xff\xff\xff\xff\xff\xff\x00\x00\xff\xff\xff\xff\xff\xff\x00\x00\xff\xff\xff\xff\xff\xff\x00\x00\xff\xff\xff\xff\xff\xff\x00\x00\xff\xff\xff\xff\xff\xff\x00\x00\xff\xff\xff\xff\xff\xff\x00\x00\xff\xff\xff\xff\xff\xff\x00\x00\xff\xff\xff\xff\xff\xff\x00\x00\xff\xff\xff\xff\xff\xff\x00\x00\xff\xff\xff\xff\xff\xff\x00\x00\xff\xff\xff\xff\xff\xff\x00\x00\xff\xff\xff\xff\xff\xff\x00\x00\xff\xff\xff\xff\xff\xff\x00\x00\xff\xff\xff\xff\xff\xff\x00\x00\xff\xff\xff\xff\xff\xff\x00\x00\xff\xff\xff\xff\xff\xff\x00\x00\xff\xff\xff\xff\xff\xff\x00\x00\xff\xff\xff\xff\xff\xff\x00\x00\xff\xff\xff\xff\xff\xff\x00\x00\xff\xff\xff\xff\xff\xff\x00\x00\xff\xff\xff\xff\xff\xff\x00\x00\xff\xff\xff\xff\xff\xff\x00\x00\xff\xff\xff\xff\xff\xff'

So is it my cheap 6-channel controller really sending just 6 channel data instead of full 512 channel data (so 6 ‘FF’ followed by 506 ’00’) or am I doing something wrong when reading? Most probably the second option, of course.

The timeout = 0.002 was just an attempt, I probably need to use 0.023 (514 frames at 250 kbps take 22.616 ms).

And I also need a 3 pin XLR male to male adapter to assure connections are stable. But if this works I expect to have a DMX BUBBL3R before Easter.

LEGO and DMX

This post is part 1 of 6 of  LEGO and DMX

Playing with MIDI, reading a lot, browsing through musical equipments… and DMX cames out.

DMX is like a custom RS-485 network. A master device sends data at 250 kHz to slave devices, each of them listening to a different “channel” or address. Typical DMX devices (“fixtures”) are stage lights but you can find fog machines, laser projectors, bubble machines, relay boards, analog interfaces…

On a studio scenario you can even join MIDI with DMX so you can create a performance combining music and lights or other kind of effects.

Generating a 250 kHz stream of data isn’t difficult, there are FTDI devices that can do that. I could use the same kind of setup I use to generate LEGO Power Functions IR signals to generate DMX signals.

In fact, people are already doing it. There are a few tutorials on how to create a DMX controller for less than $10 but I prefer this one (it costed me a bit more than 10€ but it was really straightforward, just a FTDI485 cable and a XLR 3-pin female connector.

It can be used with QLC+ (an open source DMX controller software) but that’s overkill for a MINDSTORMS EV3 so after a short search I found a python library to use it directly (dmx485).

So you connect your DYI DMX adapter to your EV3:

[1218334.312176] usb 1-1.2: New USB device found, idVendor=0403, idProduct=6001
[1218334.312248] usb 1-1.2: New USB device strings: Mfr=1, Product=2, SerialNumber=3
[1218334.312287] usb 1-1.2: Product: USB-RS485 Cable
[1218334.312322] usb 1-1.2: Manufacturer: FTDI
[1218334.312355] usb 1-1.2: SerialNumber: FT4NMHF6
[1218334.800717] usbcore: registered new interface driver usbserial
[1218334.936770] usbcore: registered new interface driver ftdi_sio
[1218334.937337] usbserial: USB Serial support registered for FTDI USB Serial Device
[1218334.938673] ftdi_sio 1-1.2:1.0: FTDI USB Serial Device converter detected
[1218334.939512] usb 1-1.2: Detected FT232RL
[1218334.956748] usb 1-1.2: FTDI USB Serial Device converter now attached to ttyUSB0

Here it is assigned to ‘/dev/ttyUSB0’

Now we just need to install ‘dmx485’:

pip3 install dmx485

(if you have a fresh ‘ev3dev’ installation you probably need to install ‘pip3’ first)

For testing, the script on ‘dmx485’ homepage is enough but I adapted it for my first python DMX script to control my cheap DMX RGB PAR spotlight (after reading the manual to find how it works – it uses 4 DMX channels for Master, Red, Green and Blue values)

!/usr/bin/env python3
import time
import dmx
import random

random.seed()

CMD = [0]*512
CMD[3] = 255  # Master Dimmer
CMD[4] = 0    # Red Dimmer
CMD[5] = 0    # Green Dimmer
CMD[6] = 0    # Blue Dimmer

BLANK = [0]*512

DELAY = 0.2
sender = dmx.DMX_Serial('/dev/ttyUSB0')
sender.start()

# WHITE
CMD[4] = 255
CMD[5] = 255
CMD[6] = 255
sender.set_data(bytes(CMD))
time.sleep(2)
sender.set_data(bytes(BLANK))

# RED dimmer
CMD[4] = 0
CMD[5] = 0
CMD[6] = 0
for i in range(0, 256, 8):
    CMD[4] = i
    sender.set_data(bytes(CMD))
    time.sleep(DELAY)
sender.set_data(bytes(BLANK))

# GREEN dimmer
CMD[4] = 0
for i in range(0, 256, 8):
    CMD[5] = i
    sender.set_data(bytes(CMD))
    time.sleep(DELAY)
sender.set_data(bytes(BLANK))

# BLUE dimmer
CMD[5] = 0
for i in range(0, 256, 8):
    CMD[6] = i
    sender.set_data(bytes(CMD))
    time.sleep(DELAY)
sender.set_data(bytes(BLANK))

# MASTER dimmer
CMD[4] = 255
CMD[5] = 255
CMD[6] = 255
for i in range(0, 256, 8):
    CMD[3] = i
    sender.set_data(bytes(CMD))
    time.sleep(DELAY)
sender.set_data(bytes(BLANK))

# RGB random
CMD[3] = 255
CMD[4] = 0
CMD[5] = 0
CMD[6] = 0
for step in range(50):
    CMD[4] = random.randint(0,255)
    CMD[5] = random.randint(0,255)
    CMD[6] = random.randint(0,255)
    print(CMD[4], CMD[5], CMD[6])
    sender.set_data(bytes(CMD))
    time.sleep(DELAY)
sender.set_data(bytes(BLANK))

It was amazingly easy.

Now the tough part is using this same USB DMX adapter to listen to DMX signals and make my own LEGO MINDSTORMS DMX fixture. I am not keen to continuously polling the bus so I probably have to spend a few more euros to buy a smarter USB DMX adapter that offloads the reception task.

mido on ev3dev

Just a quick note: got ‘mido‘ and ‘python-rtmidi‘ working together on ev3dev

So now I can use python to access MIDI ports, send and receive MIDI messages in a cleaner way, with no need for ‘amidicat’ and pipes. Not sure yet if EV3 has enough resources for my “clean” python3 scripts to work as fast as my “dirt” pybricks scripts but it’s worth a try.

It also works with ‘multimidicast’ so ipMIDI:

$ ./multimidicast &
$ python3
»»» import mido
»»» mido.get_input_name()
»»» port = mido.open_input('./multimidicast:225.0.0.37:21928 128:0')
»»» for msg in port:
»»»     print(msg)

pressing a few keys on a virtual piano on my laptop routed to a ‘qmidinet’ port and this shows up on EV3’s python shell:

...
note_on channel=0 note=81 velocity=77 time=0
note_off channel=0 note=81 velocity=0 time=0
...

so a dancing robot (or a music guided robot) is now closer to happen.

‘mido’ also have it’s own client/server solution for networking MIDI messages but I still prefer ipMIDI since I intend to keep all coding on EV3 side.

Details on installation will follow soon.:

To install ‘python-rtmidi’ there are a few dependencies:

sudo apt install pkgconf libasound2-dev

Then it should be enough (assuming we already have pip3 – if not, then “sudo apt install python3-pip“) to

sudo pip3 install python-rtmidi --install-option="--no-jack"

but it isn’t. After ~20 minutes the EV3 crashes. I resolved the problem by adding a huge swapfile and also stopping ‘avahi-daemon’ and ‘brickman’ to free up some resources, not sure what are exactly the needed resouces but this works:

sudo ev3dev-config

use this tool to disable ‘avahi-daemon’ e ‘brickman’

Then create a 128 MB file for swapfile and use it instead of the memory-based swapfile:

sudo fallocate -l 128M /swapfile
sudo chmod 600 /swapfile
sudo mkswap /swapfile
sudo swapoff -v /dev/zram0
sudo swapon /swapfile

Then the installation of ‘python-rtmidi’ should work it tooks ~25 minutes:

Successfully installed python-rtmidi-1.4.9

NOTE: I am not sure if a swapfile of 128 MB is necessary (the default swapfile on ‘/dev/zram0’ has already 96 MB) but I did test doing this with only an huge swapfile (1 GB, without disabling ‘avahi-daemon’ nor ‘brickman’) and also just disabling those 2 services and keeping the default swapfile on ‘/dev/zram0’ and neither worked.

Then ‘mido’ should be easy:

sudo pip3 install mido

After 3 or 4 minutes we get:

Successfully installed mido-1.2.10

Then we should reactive the services (‘sudo ev3dev-config’) and undo the swapfile modification:

sudo swapoff -v /swapfile
sudo rm /swapfile
sudo swapon /dev/zram0

Charlie drums

A slow drummer but nevertheless he drums:

but making he move an arm in just 2 simples steps was frustrating… spent a whole night fiddling with micropython ‘mindstorms’ library… sending script with ‘ampy’ gave stupid errors (like ‘RuntimeError: There is not a motor connected to port B.’) or even worst leading to firmware crashing so badly that I had to take the battery out of Charlie’s head at least 5 times.

So I gave up trying to use something not documented at all and reached for help from a great ROBOTMAK3R: Nard Strijbosch’s Unofficial LEGO MINDSTORMS and SPIKE Prime low-level hub module documentation

So instead of using ‘mindstorms.Motor’ I used ‘hub.port’ own motor class:

leftArm = port.B.motor
leftArm.run_for_degrees(10, speed=-100)
 while leftArm.busy(1):
     pass
 leftArm.run_for_degrees(10, speed=100)
 while leftArm.busy(1):
     pass

and now everytime I run a script through ‘ampy’ it works – except on first time after powering on Charlie’ – it seems I have to first access REPL and only after exiting it accepts ‘ampy run’ commands – if I forget to do that, Charlie turns itself off.

I am a bit disappointed with motor speed. After using the Charlie activity from the App (scratch based) I was expecting faster movements with micropython:

I need to look into the scratch demo… but I only managed to use it on my phone (on my tablet the App doesn’t load any activity) and coding is terrible on a small screen (not to mention debugging!!!).

I am not enjoying this new “paradigm”. Maybe the future is Apps and smart devices but I like to think that I can choose my own future. Can’t wait to see Pybricks running on this.

–edited–

the scratch demo controls the motors through speed only, not through position. I feel cheated 😀