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.

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 😀

Linux and the LEGO Powered Up MINDSTORMS

Nothing new here, just my personal notes with the LEGO Hub from 51515 MINDSTORMS Robot Inventor Set.

Most of it based on this post by Jason Jurotich.

I have a LEGO 51515 set, thanks to the ROBOTMAK3RS community (and still not sure yet if I am willing to pay so much money for this set).
I have an Android Lenovo tablet and an Android Samsung phone. Installed LEGO MINDSTORMS App on both but only can managed to use it on the phone – the tablet doesn’t get Activities so I could not even make Charlie play the drums.

I also have a Ubuntu Linux laptop. It will have to work – last time I revived a Windows 10 virtual machine it took me a whole night just to update it in order to install LEGO App. No way!!

So tanks to Jason post I can connect my laptop to the Hub:

sudo rfcomm connect hci0 A8:E2:C1:96:5B:9A

this gives me a ‘/dev/rfcomm0’ serial device and I can access the REPL environment in 2 different ways:

directly through a terminal client like ‘picocom’:

picocom /dev/rfcomm0 -b 115200

with a more proper tool like ‘rshell‘:

rshell -p /dev/rfcomm0 repl

When accessing the REPL, the hub is sending values so we need to Ctrl+C to get the prompt.

I found out that accessing the REPL through ‘picocom’ lead to strange behaviors afer a while (like commands being executed but not returning to the prompt and after a while not being able to access again, like if the Hub was rebooting immediately after accessing it) so I am now using ‘rshell’.

I also installed Adafruit ‘ampy’ from pypi. This way I can send and execute a micropython script from my laptop without accessing the REPL:

ampy --port /dev/rfcomm0 run test.py

But since I know nothing about LEGO micropython environment I will use REPL a while more (before flashing Pybricks on it and probably forgetting LEGO firmware like I did with MINDTORMS EV3 original application once I discovered ev3dev).

When accessing the REPL environment this is the welcome message:

Welcome to MicroPython!
 For online help please visit http://micropython.org/help/.
 Quick overview of commands for the board:
   hub.info()    -- print some general information
   hub.status()  -- print sensor data
 Control commands:
   CTRL-A        -- on a blank line, enter raw REPL mode
   CTRL-B        -- on a blank line, enter normal REPL mode
   CTRL-C        -- interrupt a running program
   CTRL-D        -- on a blank line, do a soft reset of the board
   CTRL-E        -- on a blank line, enter paste mode
   CTRL-F        -- on a blank line, enter filetransfer mode

of course ‘hub.info()’ and ‘hub.status()’ only work after we ‘import hub’ library – something probably obvious for someone used to (micro)python and REPL but not so obvious for newcomers.

Other important methods in this library

‘hub.battery.info()’:

>>> hub.battery.info()
{'temperature': 23.8, 'charge_voltage': 7591, 'charge_current': 251, 'charge_voltage_filtered': 7583, 'error_state': [0], 'charger_state': 0, 'battery_capacity_left': 80}         

‘hub.repl_restart()’ – I had used this method a few times today… and I hate reboots!

‘hub.power_off()’

So lets find out if I can make Charlie play the drum with micropython.

Back to MIDI

… and again and again 😀

So the LEGO Laser Harp v2 is almost done. A few more bricks here and there.

But it the meanwhile I had to test my MIDI ideas… and got a MiDiPLUS miniEngine USB as a cheap portable MIDI sound engineso I don’t have to use a computer (yeah… as if!)

Amazon.com: midiplus miniEngine USB MIDI Sound Module: Musical Instruments

Then I got back to the MINDSTORMS pneumatic pressure sensor idea and made a sort of LEGO MIDI Trumpet:

Then I got carried away and made my own LEGO MIDI Drum Kit:

All these using USB MIDI Adapters and plain MIDI equipment (I now also have a MIDI Merger). Even joined my MIDI keyboards to the MIDI network thanks to Patchbox OS running on a Raspberry Pi with a USB MIDI adapter for the older keyboard (DIN5) and plain USB cables for the newer ones.

But the original idea was making MIDI instruments without cables and gadgets (except for the WiFi dongle) so I got a little back again and made a few tests with multimidicast (ipMIDI).

The Drum Kit works great with MIDI cables… but extremely bad with ipMIDI when using the Raspberry Pi with Patchbox OS as an ipMIDI gateway. High latency and poor sensibility when using MODEP software generators.

So gave up MODEP and jack and used just a USB MIDI adapter to connect the ipMIDI gateway to the MiDiPLUS miniEngine USB. Much better… but still some latency.

Then… decided to try Patchbox OS own WiFi hotspot instead of my house access point. And latency droped HUGELY!

So I just need to make a few more adjustments to finish my LEGO ipMIDI Drum Kit. Then will test how this thing scales out with more ipMIDI instruments.

ev3dev and portuguese

Using ev3dev and python to speak a few words in english in english is very easy:

from ev3dev2.sound import Sound
sound = Sound()
sound.speak('Good morning')

Making it speak portuguese words is also easy:

from ev3dev2.sound import Sound
sound = Sound()
sound.speak('Bom dia', espeak_opts='-v pt')

not a great pronounce but still understandable.

Problems started when trying to speak sentences written with portuguese characters like “Olá” (“Hello”)

sound.speak('Olá', espeak_opts='-v pt')

it generates an error similar to this:

UnicodeEncodeError: 'ascii' codec can't encode character u'\xa0' in position 20: ordinal not in range(128)

my ev3dev installation wasn’t configured to understand non-ASCII characters so a quick setting at the command line corrected this:

$ export LC_ALL="en_US.UTF-8"
$ export LC_CTYPE="en_US.UTF-8"

Now I wanted to speak the current date, like ‘Today is Friday 25 December” so I found ‘datetime’

from datetime import datetime as dt
import datetime

today = dt.today()
speech = today.strftime("%A") + ' ' + today.strftime("%-d") + ' ' + today.strftime("%B")
sound.speak('Today is ' + speech)

Great. At least in english. But how to do it in portuguese (i.e. ‘Hoje é Sexta 25 Dezembro”)? How to make today.strftime(“%A) return “Sexta” instead of “Friday”?
The trick is using ‘locale’:

from datetime import datetime as dt
import datetime
import locale

locale.setlocale(locale.LC_TIME, "pt_PT") 
today = dt.today()
speech = today.strftime("%A") + ' ' + today.strftime("%-d") + ' ' + today.strftime("%B")
sound.speak('Today is ' + speech)

And now another error:

locale.Error: unsupported locale setting

ev3dev doesn’t have Portuguse locale settings (we can check with ‘locale -a‘). So I needed to install them:

$ sudo dpkg-reconfigure locales

XAC finally working

This post is part 6 of 6 of  Xbox Adaptive Controller

I kept my XAC unused for several months since it seamed broken – I could connect but nothing worked.

Until I found an issue in the Xbox Controller Driver for MacOS project saying that the XAC needs some kind of initial configuration so a connection to a Xbox or a Windows 10 machine is needed.

I spent a whole day with my Windows 10 VM and nothing – as soon as I connected the XAC to the VM it started a loop of connecting / disconnecting.

So I asked my wife if I could try it on her company’s laptop. It took just a minute, just two notifications and nothing more.

So now the XAC is working fine again with USB connection – I can even use Antimicro to assing keys to each button and use it with Pybricks IDE to remote control the LEGO Top Gear Rally Car with my feet:

Committed to Pybricks

So I committed 2 files to the Pybricks project:

not really to the project code but with a demo project that shows how to use the new ‘getchar’ function to pass commands from the Pybricks Chrome IDE to the hub in runtime.

getchar‘ works like the standard ‘input’ function but is doesn’t wait for ENTER so it is non-blocking. I already used ‘input’ to send commands to the Top Gear Rally Car and from the ‘client’ side (my EV3 python script) the only difference is not having to send a carriage return any more but from the hub side the responsiveness of the code should be now much better.

from pybricks.experimental import getchar

while True:
    c = getchar()
    if c == ord('a'):
        print('You pressed a! Now drive forwards...')
    elif c == ord('b'):
        print('You pressed b! Self destruct in 3, 2, 1...')

Another advantage of ‘getchar’ is allowing me to use Nordic nRF Toolbox UART tool to send commands to the hub:

The tool always send a termination code (we can choose LF / CR / CR+LF) but now ‘input’ considers it as just another character.

The usual companion video:

Pybricks is accepting sponsors

Have been using Pybricks a bit – with some of my MINDSTORMS EV3 (mostly because micropython is much more quick to start than full python) but also with a few Powered Up hubs I own (partly because I don’t like the idea of using Apps but also because micropython is much more clean).

Lately I realized how great this project is by offering us a common framework for the different LEGO programmable devices instead of using an App for BOOST, City and Control+ hubs and another App for SPIKE and/or Robot Inventor and python or Scratch for EV3 and who knows what LEGO will release next.

So if by any chance you are reading this you might also like to know that Pybricks is accepting support through github’s sponsors:

https://github.com/pybricks

In their own words:

“Sponsorship helps maintain this ever-growing project in the long run. Funds will go towards:

  • Porting Pybricks to existing and new LEGO hubs
  • Porting Pybricks to SPIKE Prime and MINDSTORMS Inventor
  • Supporting all compatible LEGO motors and sensors
  • Improving firmware reliability and performance
  • Improving the online programming interface
  • Writing documentation and code tutorials
  • Contributing code to other open source projects that Pybricks builds on

Please consider becoming a sponsor if you enjoy using Pybricks in your creations!”