Running LEGO LDD on linux

I’m finally going to try the EV3DPrinter.

3D pen

Now that my 3D pen arrived from China I downloaded Marc-André Bazergui LDD file to understand how to assemble it and then it striked me… dang, need Windows to run LDD!

I still have the Windows VM I used to update the firmware of my EV3 but I don’t want to use it (yes, I’m stubborn) so I decided to try wine. I once had LDD working with wine but never really used it and now that I got a new laptop I didn’t even bothered to install wine again.

So after a few tweaks I got LDD running – it seems that running 32-bit MS Windows programs on wine on a 64-bit linux breaks some things but essentially one just needs to add some 32-bit gstreamer plugins to make LDD work fine.

To show the full process I created a 64-bit virtual machine (1 CPU, 4 GB RAM, 32 GB thin provisioned disk), installed Ubuntu 16.10 (64-bit) on it (default installation, just enabled the download of updates while installing and the installation of 3rd party software).

As I’m using VirtualBox I also installed the VirtualBox Guest Addictions, enabled bi-directional clipboard to allow copy&past of commands between the VM and my desktop and enabled a shared folder to exchange files (just the LDD 4.3.10 setup file and the EV3DPrinter .lxf file).

Then a full last update:

sudo apt update
sudo apt upgrade
sudo apt dist-upgrade

followed by a reboot and a safety snapshot (“trust no one”).

So this is the full process:

sudo dpkg --add-architecture i386 
sudo add-apt-repository ppa:wine/wine-builds
sudo apt-get update
sudo apt-get install --install-recommends winehq-devel

at this moment, I have wine 2.4 installed:

wine --version
wine-2.4

I could install LDD right now but it will not work because at first run it tries to play some music and or video and it fails. The trick is to install some plugins for gstreamer:

sudo apt install gstreamer1.0-plugins-good:i386 gstreamer1.0-fluendo-mp3:i386

So we install LDD by just double-clicking it. As it is the first time wine runs, it first asks to install two dependencies: mono and gecko (that assures some .Net Framework and Internet Explorer compatibility).

LDD setup asks for a language (“English”) then asks us to accept the License Agreement and suggests creating two shortcuts (“Desktop” and “Quick lauch”).

Then it asks to install Adobe Flash Player and to choose a destination folder (default is fine).

When completed, we may check the option to “Run LEGO Digital Designer” but it will not work, it just shows a black window that we need to force close.

But if we launch LDD again, it works now.

Just a last issue: when opening the EV3DPrinter .lxf file we get a request for a FLEXnet license file, it is located at the installation folder:

~/.wine32/drive_c/Program Files/LEGO Company/LEGO Digital Designer/RL278-1000.lic

Everything seems to work, even creating a Building Guide and the HTML Building Instructions.

I recorded everything in this video:

It’s a long (21 min) non edited video so you may want to skip most of it (the download and installation of wine components, the install of LDD and the creation of the Building Guide).

And by the way, this is nothing really new – Marc pointed me this video with LDD running on Ubuntu 7.10 (2007!)

Virtual Mindstorms – using LEGO EV3 software on Linux

Yesterday Marc-André Bazergui incentivized me to make a video showing how to use LEGO MINDSTORMS EV3 Software inside a virtual machine. It is a shame that a product running Linux inside can only be used on PC or Mac – and that’s one of the reasons I started using ev3dev as I only have linux systems (laptops, Raspberry Pi’s, old DIY desktops without a Windows license…).

I got my first EV3 exactly 3 years ago as a birthday gift from my wife. I don’t remember if I ever installed the Windows software on a VM before – I did installed one or twice in Ubuntu with Wine (not sure why) and I did installed a Microsoft Robotics Developer Studio in a VMware Workstation virtual machine and do remember having connected it to the the EV3 thorough a bluetooth USB dongle (most modern hypervisors have this nice feature to allow a local device on the host to be passed-through into the guest).

I no longer have VMware Workstation but I have used Innotek VirtualBox in the past and knew that Oracle somehow managed to keep it alive after buying it (Oracle has the morbid habit of poisoning every good thing it owns – Java, Solaris, OpenOffice, MySQL…).

So I installed Oracle VM VirtualBox 5.1.4 (there is even a x64 .deb package for Ubuntu 16.04 “Xenial”) and after that the VirtualBox 5.1.4 Oracle VM VirtualBox Extension Pack.

It was quite easy and also very fast. After that I got a licensed version of Microsoft Windows 8 Professional (x64 also) – this is my work laptop so people immediatlely started making fun of me – hey, he is installing Windows on his laptop, finally!

The rest of the process was also quite easy after all – like I thought, it is possible to use a Bluetooth USB dongle and also just the direct USB cable connection:

  • create a Virtual Machine
  • make sure “Enable USB Controller” is checked and USB 2.0 (EHCI) Controller is selected – it might also work with USB 3.0
  • add an USB Device Filter for each USB device you want to passthrough into the VM (the EV3 itself and/or the Bluetooth dongle)
  • install Windows
  • present VirtualBox Guest Additions CD Image and install
  • define a Shared Folder so you can pass drivers and binaries into the VM
  • if the Bluetooth dongle is not automatic configured, install the proper drivers
  • pair the EV3 (or plug the USB cable)
  • install LEGO Mindstorms EV3 software and run it

I made a video showing every step (just skipped the LEGO Software as it’s pretty straightfoward):

Just one note: although USB cable connection seems to work fine, i tried to upgrade my EV3 firmware several times with no success – every single time it hangs at 0%. Perhaps it behaves better with another Windows version… who knows?

Edit: Laurens Valk and David Lechner know. So I made a second post showing how to upgrade the firmware.

LEGO laser harp – part II

This post is part 2 of 2 of  LEGO Laser Harp

About 10 years ago I offered my wife a M-Audio USB MIDI Keyboard and installed Ubuntu Studio on a computer so she could play some piano at home. She was so amazed with the possibility to generate music sheet while playing that almost accepted the idea of using Linux… almost 🙂

I remember that at that time I used timidity++ as a software MIDI synthesizer, tuned ALSA (one of the several Linux sound systems, perhaps the most generally used) and the preemptive kernel to work almost flawlessly with the Creative Labs sound card. My wife didn’t enjoy the KDE experience, Firefox was OK for her but OpenOffice were terribly with the spreadsheets she used and finally, when our first kid was born, she attended some English lessons at Wall Street Institute and we found out that the online lessons required an odd combination of an old version on Java, ActiveX and IE… so she returned to Windows XP and never looked back.

10 years is a LOT of time in computer systems but ALSA is still around, even on ev3dev. So I installed timidity++ and tried to play a MIDI file… to find that an ALSA module that is not currently available in ev3dev kernel is required just for MIDI.

I googled for alternatives and found fluidsynth with an unexpected bonus: there is a quite interesting python library, mingus, that works with fluidsynth. So I installed it in my Ubuntu laptop and in a few minutes I was playing harp – amazing!

sudo apt-get install fluidsynthsudo easy_install mingus
python
>>> from mingus.midi import fluidsynth
>>> from mingus.containers.note import Note
>>> fluidsynth.init("/usr/share/sounds/sf2/FluidR3_GM.sf2", "alsa")
>>> fluidsynth.set_instrument(1, 46)
>>> fluidsynth.play_Note(Note("C-3"))

In the previous example I just import the fluidsynth and Note parts of the library, initialize fluidsynth to work with ALSA loading the soundfount that cames with it, choose harp (instrument number 46) and play C3.

Well and polyphony? The correct way is to use a NoteContainer

from mingus.containers import NoteContainer
fluidsynth.play_NoteContainer(NoteContainer(["B-3", "C-3", "F-3"]))

but the lazy way is… just play several notes in a fast sequence.

So, let’s do it in the ev3dev!

Oops, fluidsynth also needs an ALSA module not available in current ev3dev kernel.

I’m not a linux music expert. Not even a linux expert! So after some more googling I gave up and asked for help in ev3dev’ GitHub project. And once again David accepted to include ALSA MIDI suport in future kernels, so I’ll just wait a bit.

Oh, but I can’t wait…

And if I read the color sensors in ev3dev and play the music in my laptop?

ALSA, once again, suports something like client/server MIDI communication with “aseqnet” and “aconnect” commands and some people are already using it with Raspberry Pi!

Yeah, I should have guessed… “aconnect” requires an ALSA MIDI module that is not available in current ev3dev kernel.

OK, let’s use MQTT: configure my EV3 as a publisher and my Ubuntu laptop as a subscriber and just send some notes as messages.

On the EV3:

sudo apt-get install mosquitto
sudo easy_install paho-mqtt

The publisher script is “harp-mqtt-pub.py”:

#!/usr/bin/env python

from ev3dev.auto import *
from time import sleep
import paho.mqtt.client as mqtt

DELAY = 0.01

# should have an auto-calibrate function
AMB_THRESHOLD = 9

sensor1 = ColorSensor('in1:i2c80:mux1')
sensor1.mode = 'COL-AMBIENT'
sensor2 = ColorSensor('in1:i2c81:mux2')
sensor2.mode = 'COL-AMBIENT'
sensor3 = ColorSensor('in1:i2c82:mux3')
sensor3.mode = 'COL-AMBIENT'
sensor4 = ColorSensor('in2')
sensor4.mode = 'COL-AMBIENT'
sensor5 = ColorSensor('in3')
sensor5.mode = 'COL-AMBIENT'
sensor6 = ColorSensor('in4')
sensor6.mode = 'COL-AMBIENT'

# there is no sensor7 yet, I need another MUX

s1 = 0
s2 = 0
s3 = 0
s4 = 0
s5 = 0
s6 = 0
s7 = 0

client = mqtt.Client()
client.connect("localhost",1883,60)

print 'Running...'

while True:
    key_touched = False
    s1 = sensor1.value(0)
    s2 = sensor2.value(0)
    s3 = sensor3.value(0)
    s4 = sensor4.value(0)
    s5 = sensor5.value(0)
    s6 = sensor6.value(0)
#    s7 = sensor7.value(0)

    if s1 < AMB_THRESHOLD:
        client.publish("topic/Harp", "C-3")
        key_touched=True
    if s2 < AMB_THRESHOLD:
        client.publish("topic/Harp", "D-3")
        key_touched=True
    if s3 < AMB_THRESHOLD:
        client.publish("topic/Harp", "E-3")
        key_touched=True
    if s4 < AMB_THRESHOLD:
        client.publish("topic/Harp", "F-3")
        key_touched=True
    if s5 < AMB_THRESHOLD:
        client.publish("topic/Harp", "G-3")
        key_touched=True
    if s6 < AMB_THRESHOLD:
        client.publish("topic/Harp", "A-3")
        key_touched=True
#    if s7 < AMB_THRESHOLD:
#        client.publish("topic/Harp", "B-3")
#        key_touched=True

    if key_touched == True:
        sleep(DELAY)

On the Ubuntu laptop side:

sudo easy_install paho-mqtt

The subscriber script is “harp-mqtt-sub.py”

#!/usr/bin/env python

import paho.mqtt.client as mqtt
from mingus.midi import fluidsynth
from mingus.containers.note import Note

EV3_IP = "192.168.43.35"

SOUNDFONT = 'Concert_Harp.sf2'
INSTRUMENT = 46 # Harp

NOTES = ['C-3','D-3','E-3','F-3','G-3','A-3','B-3']

def on_connect(client, userdata, flags, rc):
    print("Connected with result code "+str(rc))
    client.subscribe("topic/Harp")

def on_message(client, userdata, msg):
    global i
    if (msg.payload in NOTES):
        print msg.payload
        fluidsynth.play_Note(Note(msg.payload))
    
client = mqtt.Client()
client.connect(EV3_IP,1883,60)

client.on_connect = on_connect
client.on_message = on_message

fluidsynth.init(SOUNDFONT, "alsa")
fluidsynth.set_instrument(1, INSTRUMENT)

client.loop_forever()

And guess what? It works!!! I just love linux and open source!

I will keep waiting for David Lechner to include ALSA MIDI support in ev3dev’ kernel. I’m not so sure if there is enough horsepower in the EV3 to load a soundfont and play it with acceptable latency but if I can at least use the MIDI client/server functionality I can drop MQTT.

An interesting possibility that this client/server design allows is to scale my harp easily: with just a second EV3 (2 MUX each) I can make a 13-string harp with almost no modification on my code.

LEGO WeDo 2.0 – playing sound

This post is part 4 of 6 of  WeDo 2.0 - reverse engineering

Great news – LEGO Eduction released the WeDo 2.0 SDK today!

After digging into it, I found the information needed to control the Piezo: as expected, it’s controlled by the same handle that is used for controlling the motor and the RGB LED (0x003d). The “port” is “05” and the “command” to activate the Piezo is “02”, followed by a payload of “04” bytes containing:

  • the Frequency in Hz (2 bytes, reversed)
  • the duration in ms (2 bytes, reversed)

So to play a “C” (or “Do”, 261 Hz) during 1/8 of a second (125 ms) we use this command:

[A0:E6:F8:1E:58:57][LE]> char-write-cmd 003d 050204B801E803

So let’s hear the very first music played by a WeDo 2.0 from a linux shell script:

#!/usr/bin/env bash

# In Ubuntu run this script with sudo
# "Imperial March on a WeDo 2.0" was inspired by https://gist.github.com/tagliati/1804108

# command: gatttool -i hci0 -b A0:E6:F8:1E:58:57 --char-write-req -a 0x003d -n 050204

# beep(a, 500) 
gatttool -i hci0 -b A0:E6:F8:1E:58:57 --char-write-req -a 0x003d -n 050204B801F401
sleep 0.5

# beep(a, 500) 
gatttool -i hci0 -b A0:E6:F8:1E:58:57 --char-write-req -a 0x003d -n 050204B801F401
sleep 0.5

# beep(a, 500) 
gatttool -i hci0 -b A0:E6:F8:1E:58:57 --char-write-req -a 0x003d -n 050204B801F401
sleep 0.5

# beep(f, 350)
gatttool -i hci0 -b A0:E6:F8:1E:58:57 --char-write-req -a 0x003d -n 0502045D015E01
sleep 0.35

# beep(cH, 150)
gatttool -i hci0 -b A0:E6:F8:1E:58:57 --char-write-req -a 0x003d -n 0502040B029600
sleep 0.15

# beep(a, 500)
gatttool -i hci0 -b A0:E6:F8:1E:58:57 --char-write-req -a 0x003d -n 050204B801F401
sleep 0.5

# beep(f, 350)
gatttool -i hci0 -b A0:E6:F8:1E:58:57 --char-write-req -a 0x003d -n 0502045D015E01
sleep 0.35

# beep(cH, 150)
gatttool -i hci0 -b A0:E6:F8:1E:58:57 --char-write-req -a 0x003d -n 0502040B029600
sleep 0.15

# beep(a, 1000)
gatttool -i hci0 -b A0:E6:F8:1E:58:57 --char-write-req -a 0x003d -n 050204B801E803
sleep 1.0

# beep(eH, 500)
gatttool -i hci0 -b A0:E6:F8:1E:58:57 --char-write-req -a 0x003d -n 0502049302F401
sleep 0.5
    
# beep(eH, 500)
gatttool -i hci0 -b A0:E6:F8:1E:58:57 --char-write-req -a 0x003d -n 0502049302F401
sleep 0.5

# beep(eH, 500)
gatttool -i hci0 -b A0:E6:F8:1E:58:57 --char-write-req -a 0x003d -n 0502049302F401
sleep 0.5

# beep(fH, 350) 
gatttool -i hci0 -b A0:E6:F8:1E:58:57 --char-write-req -a 0x003d -n 050204BA025E01
sleep 0.35

# beep(cH, 150)
gatttool -i hci0 -b A0:E6:F8:1E:58:57 --char-write-req -a 0x003d -n 0502040B029600
sleep 0.15

# beep(gS, 500)
gatttool -i hci0 -b A0:E6:F8:1E:58:57 --char-write-req -a 0x003d -n 0502049F01F401
sleep 0.5    

# beep(f, 350)
gatttool -i hci0 -b A0:E6:F8:1E:58:57 --char-write-req -a 0x003d -n 0502045D015E01
sleep 0.35

# beep(cH, 150)
gatttool -i hci0 -b A0:E6:F8:1E:58:57 --char-write-req -a 0x003d -n 0502040B029600
sleep 0.15

# beep(a, 1000)
gatttool -i hci0 -b A0:E6:F8:1E:58:57 --char-write-req -a 0x003d -n 050204B801E803
sleep 1.0
 
# beep(aH, 500)   
gatttool -i hci0 -b A0:E6:F8:1E:58:57 --char-write-req -a 0x003d -n 0502047003F401
sleep 0.5

# beep(a, 350) 
gatttool -i hci0 -b A0:E6:F8:1E:58:57 --char-write-req -a 0x003d -n 050204B8015E01
sleep 0.35

# beep(a, 150)
gatttool -i hci0 -b A0:E6:F8:1E:58:57 --char-write-req -a 0x003d -n 050204B8019600
sleep 0.15

# beep(aH, 500)   
gatttool -i hci0 -b A0:E6:F8:1E:58:57 --char-write-req -a 0x003d -n 0502047003F401
sleep 0.5

# beep(gSH, 250)
gatttool -i hci0 -b A0:E6:F8:1E:58:57 --char-write-req -a 0x003d -n 0502043E03FA00
sleep 0.25

# beep(gH, 250)
gatttool -i hci0 -b A0:E6:F8:1E:58:57 --char-write-req -a 0x003d -n 0502041003FA00
sleep 0.25

# beep(fSH, 125)
gatttool -i hci0 -b A0:E6:F8:1E:58:57 --char-write-req -a 0x003d -n 050204E4027D00
sleep 0.125

# beep(fH, 125) 
gatttool -i hci0 -b A0:E6:F8:1E:58:57 --char-write-req -a 0x003d -n 050204BA027D00
sleep 0.125  
   
# beep(fSH, 250)
gatttool -i hci0 -b A0:E6:F8:1E:58:57 --char-write-req -a 0x003d -n 050204E402FA00
sleep 0.25

# delay(250)
sleep 0.25

# beep(aS, 250)
gatttool -i hci0 -b A0:E6:F8:1E:58:57 --char-write-req -a 0x003d -n 050204C701FA00
sleep 0.25

# beep(dSH, 500)
gatttool -i hci0 -b A0:E6:F8:1E:58:57 --char-write-req -a 0x003d -n 0502046E02F401
sleep 0.5

# beep(dH, 250)  
gatttool -i hci0 -b A0:E6:F8:1E:58:57 --char-write-req -a 0x003d -n 0502044B02FA00
sleep 0.25

# beep(cSH, 250)  
gatttool -i hci0 -b A0:E6:F8:1E:58:57 --char-write-req -a 0x003d -n 0502042A02FA00
sleep 0.25

# beep(cH, 125)
gatttool -i hci0 -b A0:E6:F8:1E:58:57 --char-write-req -a 0x003d -n 0502040B027D00
sleep 0.125

# beep(b, 125)
gatttool -i hci0 -b A0:E6:F8:1E:58:57 --char-write-req -a 0x003d -n 050204D2017D00
sleep 0.125

# beep(cH, 250)
gatttool -i hci0 -b A0:E6:F8:1E:58:57 --char-write-req -a 0x003d -n 0502040B02FA00
sleep 0.25
      
# delay(250)
sleep 0.25

# beep(f, 125)
gatttool -i hci0 -b A0:E6:F8:1E:58:57 --char-write-req -a 0x003d -n 0502045D017D00
sleep 0.125

# beep(gS, 500)
gatttool -i hci0 -b A0:E6:F8:1E:58:57 --char-write-req -a 0x003d -n 0502049F01F401
sleep 0.5

# beep(f, 375) 
gatttool -i hci0 -b A0:E6:F8:1E:58:57 --char-write-req -a 0x003d -n 0502045D017701
sleep 0.375

# beep(a, 125)
gatttool -i hci0 -b A0:E6:F8:1E:58:57 --char-write-req -a 0x003d -n 050204B8017D00
sleep 0.125
  
# beep(cH, 500)
gatttool -i hci0 -b A0:E6:F8:1E:58:57 --char-write-req -a 0x003d -n 0502040B02F401
sleep 0.5

# beep(a, 375)
gatttool -i hci0 -b A0:E6:F8:1E:58:57 --char-write-req -a 0x003d -n 050204B8017701
sleep 0.375

# beep(cH, 125)
gatttool -i hci0 -b A0:E6:F8:1E:58:57 --char-write-req -a 0x003d -n 0502040B027D00
sleep 0.125

# beep(eH, 1000)
gatttool -i hci0 -b A0:E6:F8:1E:58:57 --char-write-req -a 0x003d -n 0502049302E803
sleep 1.0

# beep(aH, 500)   
gatttool -i hci0 -b A0:E6:F8:1E:58:57 --char-write-req -a 0x003d -n 0502047003F401
sleep 0.5

# beep(a, 350) 
gatttool -i hci0 -b A0:E6:F8:1E:58:57 --char-write-req -a 0x003d -n 050204B8015E01
sleep 0.35

# beep(a, 150)
gatttool -i hci0 -b A0:E6:F8:1E:58:57 --char-write-req -a 0x003d -n 050204B8019600
sleep 0.15

# beep(aH, 500)   
gatttool -i hci0 -b A0:E6:F8:1E:58:57 --char-write-req -a 0x003d -n 0502047003F401
sleep 0.5

# beep(gSH, 250)
gatttool -i hci0 -b A0:E6:F8:1E:58:57 --char-write-req -a 0x003d -n 0502043E03FA00
sleep 0.25

# beep(gH, 250)
gatttool -i hci0 -b A0:E6:F8:1E:58:57 --char-write-req -a 0x003d -n 0502041003FA00
sleep 0.25

# beep(fSH, 125)
gatttool -i hci0 -b A0:E6:F8:1E:58:57 --char-write-req -a 0x003d -n 050204E4027D00
sleep 0.125
    
# beep(fH, 125) 
gatttool -i hci0 -b A0:E6:F8:1E:58:57 --char-write-req -a 0x003d -n 050204BA027D00
sleep 0.125

# beep(fSH, 250)
gatttool -i hci0 -b A0:E6:F8:1E:58:57 --char-write-req -a 0x003d -n 050204E402FA00
sleep 0.25

# delay(250)
sleep 0.25

# beep(aS, 250)
gatttool -i hci0 -b A0:E6:F8:1E:58:57 --char-write-req -a 0x003d -n 050204C701FA00
sleep 0.25

# beep(dSH, 500)
gatttool -i hci0 -b A0:E6:F8:1E:58:57 --char-write-req -a 0x003d -n 0502046E02F401
sleep 0.5

# beep(dH, 250)  
gatttool -i hci0 -b A0:E6:F8:1E:58:57 --char-write-req -a 0x003d -n 0502044B02FA00
sleep 0.25

# beep(cSH, 250)  
gatttool -i hci0 -b A0:E6:F8:1E:58:57 --char-write-req -a 0x003d -n 0502042A02FA00
sleep 0.25

# beep(cH, 125)
gatttool -i hci0 -b A0:E6:F8:1E:58:57 --char-write-req -a 0x003d -n 0502040B027D00
sleep 0.125

# beep(b, 125)
gatttool -i hci0 -b A0:E6:F8:1E:58:57 --char-write-req -a 0x003d -n 050204D2017D00
sleep 0.125

# beep(cH, 250)
gatttool -i hci0 -b A0:E6:F8:1E:58:57 --char-write-req -a 0x003d -n 0502040B02FA00
sleep 0.25

# delay(250)
sleep 0.25

# beep(f, 250)
gatttool -i hci0 -b A0:E6:F8:1E:58:57 --char-write-req -a 0x003d -n 0502045D01FA00
sleep 0.25

# beep(gS, 500)
gatttool -i hci0 -b A0:E6:F8:1E:58:57 --char-write-req -a 0x003d -n 0502049F01F401
sleep 0.5 
  
# beep(f, 375)
gatttool -i hci0 -b A0:E6:F8:1E:58:57 --char-write-req -a 0x003d -n 0502045D017701
sleep 0.375

# beep(cH, 125)
gatttool -i hci0 -b A0:E6:F8:1E:58:57 --char-write-req -a 0x003d -n 0502040B027D00
sleep 0.125
           
# beep(a, 500)
gatttool -i hci0 -b A0:E6:F8:1E:58:57 --char-write-req -a 0x003d -n 050204B801F401
sleep 0.5

# beep(f, 375)
gatttool -i hci0 -b A0:E6:F8:1E:58:57 --char-write-req -a 0x003d -n 0502045D017701
sleep 0.375

# beep(c, 125)
gatttool -i hci0 -b A0:E6:F8:1E:58:57 --char-write-req -a 0x003d -n 05020405017D00
sleep 0.125

# beep(a, 1000)
gatttool -i hci0 -b A0:E6:F8:1E:58:57 --char-write-req -a 0x003d -n 050204B801E803
sleep 1.0
 

 

 

Controlo de SBrick com wiimote

Descobri que é muito fácil usar um wiimote em Linux por isso na sequência de ‘SBrick – controlo remoto com um gamepad‘ segue agora como controlar o SBrick com um wiimote (na verdade um clone barato, um N-Play Remote Plus, comprado fora de horas numa Worten – infelizmente não havia a versão com Motion Plus).

O wiimote usa Bluetooth, apesar de não seguir estritamente as normas. Se tivermos bluetooth no nosso PC podemos confirmar que estamos em condições de utilizar o wiimote carregando em simultâneo nos botões ‘1’ e ‘2’ do wiimote para que este fique visível durante uns segundos:

$ hcitool -i hci0 scan
Scanning ...
    04:02:16:01:1C:E7    Nintendo RVL-CNT-01

Como não segue as normas não é possível emparelhar com ele mas existem ferramentas para isso como o cwiid. Como vou usar pyhton fui buscar a library correspondente:

$ sudo apt-get install python-cwiid

existem vários exemplos básicos que não vou apresentar aqui, sigo directamente para o resultado final:

 

O script utilizado no video acima:

# apt-get install python-cwiid
import cwiid
from time import sleep
from subprocess import call
from math import log10

# macros for the SBrick
DRIVE_A="gatttool -b 00:07:80:7F:28:E1 -i hci0 --char-write --handle=0x0025 --value=0102"
DRIVE_B="gatttool -b 00:07:80:7F:28:E1 -i hci0 --char-write --handle=0x0025 --value=0103"
COAST_A="gatttool -b 00:07:80:7F:28:E1 -i hci0 --char-write --handle=0x0025 --value=01020000"
COAST_B="gatttool -b 00:07:80:7F:28:E1 -i hci0 --char-write --handle=0x0025 --value=01030000"
BREAK_A="gatttool -b 00:07:80:7F:28:E1 -i hci0 --char-write --handle=0x0025 --value=0002"
BREAK_B="gatttool -b 00:07:80:7F:28:E1 -i hci0 --char-write --handle=0x0025 --value=0003"


# connecting to the wiimote. This allows several attempts
# as first few often fail.

print 'Press 1+2 on your Wiimote now...'
wm = None
i=1
while not wm:
    try:
        wm=cwiid.Wiimote()
    except RuntimeError:
        if (i>5):
            print("cannot create connection")
            quit()
        print "Error opening wiimote connection"
        print "attempt " + str(i)
        i +=1

#set wiimote to report button presses and accelerometer state
wm.rpt_mode = cwiid.RPT_BTN | cwiid.RPT_ACC

#turn on all led and ruble to show connected
wm.led=15
wm.rumble=True
sleep(0.5)
wm.rumble=False
wm.led=0
sleep(1.5)

# roll = accelerometer[0], standby ~125
# pitch = accelerometer[1], standby ~125

while True:
    buttons = wm.state['buttons']

    #only pay attention when button '1' pressed
    if (buttons & cwiid.BTN_1):

        roll=(wm.state['acc'][0]-125)
        pitch=(wm.state['acc'][1]-125)
        
        if (roll<0):
            if (roll<-4):
                if (roll<-25):
                    roll=-100
                else:
                    roll=-50*log10(-4*roll)
            else:
                roll=0

        if (roll>0):
            if (roll>4):
                if (roll>25):
                    roll=100
                else:
                    roll=50*log10(4*roll)
            else:
                roll=0

        if (pitch>0):
            if (pitch>4):
                if (pitch>25):
                    pitch=100
                else:
                    pitch=50*log10(4*pitch)

            else:
                pitch=0

        if (pitch<0):
            if (pitch<-4):
                if(pitch<-25):
                    pitch=-100
                else:
                    pitch=-50*log10(-4*pitch)
            else:
                pitch=0

        if ((pitch<>0)or(roll<>0)):

            roll=2.5*roll
            pitch=2.5*pitch

            if(pitch<>0):
                if(roll>0):
                    # turn right
                    motor_L=pitch
                    motor_R=-pitch-roll/2

                else:
                    # turn left
                    motor_R=-pitch
                    motor_L=pitch+roll/2

            elif(roll<>0):
                #just rotate
                motor_R=motor_L=roll;

            else:
                # does nothing
                motor_R=motor_L=0

            if((motor_R<>0)or(motor_L<>0)):

                if(motor_R<0):
                    duty=str(hex(int(-motor_R)))
                    command_A=DRIVE_A+"00"+duty[2:]                    
                else:
                    duty=str(hex(int(motor_R)))
                    command_A=DRIVE_A+"01"+duty[2:]

                if(motor_L<0):
                    duty=str(hex(int(-motor_L)))
                    command_B=DRIVE_B+"00"+duty[2:]                    
                else:
                    duty=str(hex(int(motor_L)))
                    command_B=DRIVE_B+"01"+duty[2:]

                #send motors commands to SBrick
                call(command_A, shell=True);
                call(command_B, shell=True);
                sleep(0.1)

                #send COAST commands to SBrick
                call(COAST_A, shell=True);
                call(COAST_B, shell=True);

    else:
        # inactive
        sleep(0.01)

SBrick – controlo remoto com um gamepad

Apresento o meu script em python para controlar o SBrick com um gamepad a partir do Linux. Recorro à biblioteca PyGame para ler o gamepad (assumindo que o gamepad é suportado nativamente pelo Linux, ver também o meu artigo sobre como utiizar um gamepad com ev3dev) e ao comando gatttool do BlueZ 5.0 para comunicar via Bluetooth BLE com o SBrick (assumindo também a presença de um dongle Bluetooth 4.0).

 

Este script funciona bem com Ubuntu mas deverá também funcionar em qualquer variante de Debian incluindo Raspbian (no Raspberry Pi) e ev3dev (no LEGO Mindstorms EV3, onde utilizei uma versão inicial deste script).

#!/usr/bin/env python

# sudo apt-get install python-pygame

import sys, traceback, os
os.environ['SDL_VIDEODRIVER'] = 'dummy'
from math import log10
from subprocess import call
from time import sleep
from pygame import joystick, event, display

### buttons ###
B_TRIANG = 0
B_CIRC = 1
B_CROSS = 2
B_SQUARE = 3
B_LTRIG2 = 4
B_RTRIG2 = 5
B_LTRIG = 6
B_RTRIG = 7
B_SELECT = 8
B_LJOY = 10
B_RJOY = 11
B_START = 9


def main():
  try:
    display.init();
    joystick.init();
    js=joystick.Joystick(0);
    js.init();

    DRIVE_A="gatttool -b 00:07:80:7F:28:E1 -i hci0 --char-write --handle=0x0025 --value=0102"
    DRIVE_B="gatttool -b 00:07:80:7F:28:E1 -i hci0 --char-write --handle=0x0025 --value=0103"
    COAST_A="gatttool -b 00:07:80:7F:28:E1 -i hci0 --char-write --handle=0x0025 --value=01020000"
    COAST_B="gatttool -b 00:07:80:7F:28:E1 -i hci0 --char-write --handle=0x0025 --value=01030000"
    BREAK_A="gatttool -b 00:07:80:7F:28:E1 -i hci0 --char-write --handle=0x0025 --value=0002"
    BREAK_B="gatttool -b 00:07:80:7F:28:E1 -i hci0 --char-write --handle=0x0025 --value=0003"

    ### starts in Joystick mode ###
    control_by_JOYSTICK=True;

    num_axes=js.get_numaxes();
    num_buttons=js.get_numbuttons();
    num_hats=js.get_numhats();

    ### assuming 4 axes, 13 buttons and 1 hat

    flag=False;

    while True:
      x=y=motor_r=motor_l=0.0;
      event.pump();

      button_mode=js.get_button(B_SELECT);
      button_shot=js.get_button(B_SQUARE);

      if button_mode ==1:

        if control_by_JOYSTICK==True:
          control_by_JOYSTICK=False;
          print 'Control Mode=HAT';
        else:
          control_by_JOYSTICK=True;
          print 'Control Mode=JOYSTICK';


      ### joysticks axis [-1, +1]
      ### x=axis2 , y=-axis3
      ### ignore less than 0.2 (dead zone)
      ### apply log10(100x) (to reforce lower values)
      ### result is less or equal than 2 = log10(100)

      if control_by_JOYSTICK==True:

            # Control by Right Joystick, Axis 2 e 3

            axis2=js.get_axis(2);
            axis3=js.get_axis(3);

        if axis2>0:
          if axis2<0.2:
            x=0;
          else:
            x=log10(axis2*100);
        elif axis2<0:
          if axis2>-0.2:
            x=0;
          else:
            x=-log10(-axis2*100);
            else:
              x=0;

        if axis3>0:
          if axis3<0.2:
            y=0;
          else:
            y=-log10(axis3*100);
        elif axis3<0:
          if axis3>-0.2:
            y=0;
          else:
            y=log10(-axis3*100);
        else:
          y=0;

        if y<>0:
          if x<0:
            motor_r=100*y;
            # turn left => slow motor_l
               motor_l=y*(100+25*x);
          else:
            motor_el=100*y;
            # turn right => slow motor_r
            motor_r=y*(100-25*x);
        elif x<>0:
           # y=0, just turn
           motor_l=100*x;
           motor_r=-motor_l;

      else:

         # Control by HAT keys

         hat=js.get_hat(0);

         if hat==(0,1):
#            print 'FRONT';
            motor_r=100;
            motor_l=100;
          elif hat==(1,0):
#            print 'RIGHT';
            motor_l=100;
            motor_r=-100;
         elif hat==(0,-1):
#            print 'BACK';
            motor_r=-100;
            motor_l=-100;
         elif hat==(-1,0):
#            print 'LEFT';
            motor_l=-100;
            motor_r=100;
         elif hat==(1,1):
#            print 'FRONT+RIGHT';
            motor_l=100;
            motor_r=50;
         elif hat==(-1,1):
#            print 'FRONT+LEFT';
            motor_l=50;
            motor_r=100;
         elif hat==(-1,-1):
#            print 'BACK+LEFT';
            motor_l=-100;
            motor_r=-50;
         elif hat==(1,-1):
#            print 'BACK+RIGHT';
            motor_l=-50;
            motor_r=-100;


       # get direction and duty cycle

      if (motor_l<0):
     dir_l="00"
     duty_l=str(hex(int(-motor_l)))
      else:
         dir_l="01"
         duty_l=str(hex(int(motor_l)))

      if (motor_r<0):
     dir_r="01"
     duty_r=str(hex(int(-motor_r)))
      else:
         dir_r="00"
         duty_r=str(hex(int(motor_r)))

      # command+direction+dutycyle

      command_A=DRIVE_A+dir_r+duty_r[2:]
      command_B=DRIVE_B+dir_l+duty_l[2:]
      call(command_A, shell=True);
      call(command_B, shell=True);
      sleep(0.1)
#      call(BREAK_A,shell=True);
#      call(BREAK_B,shell=True);
      call(COAST_A,shell=True);
      call(COAST_B,shell=True);

    # end while

  except (KeyboardInterrupt, SystemExit):
      print "Exiting...";
  except Exception:
      traceback.print_exc(file=sys.stdout);

  js.quit();
  joystick.quit();
  display.quit();
  sys.exit(0);

if __name__ == "__main__":
    main()

SBrick – remote control with a wireless gamepad

Here is my python script for controlling SBrick with a gamepad from Linux. It uses pygame for reading the gamepad (as long as it’s supported by the kernel, see also my post about using a gamepad with ev3dev) and gatttool from BlueZ 5.x to talk to the SBrick (you need a BT 4.0 USB dongle)

It should work in Ubuntu and other Debian variants including Raspbian (Raspberry Pi) or ev3dev (LEGO Mindstorms EV3)

#!/usr/bin/env python

# sudo apt-get install python-pygame

import sys, traceback, os
os.environ['SDL_VIDEODRIVER'] = 'dummy'
from math import log10
from subprocess import call
from time import sleep
from pygame import joystick, event, display

### buttons ###
B_TRIANG = 0
B_CIRC = 1
B_CROSS = 2
B_SQUARE = 3
B_LTRIG2 = 4
B_RTRIG2 = 5
B_LTRIG = 6
B_RTRIG = 7
B_SELECT = 8
B_LJOY = 10
B_RJOY = 11
B_START = 9


def main():
  try:
    display.init();
    joystick.init();
    js=joystick.Joystick(0);
    js.init();

    DRIVE_A="gatttool -b 00:07:80:7F:28:E1 -i hci0 --char-write --handle=0x0025 --value=0102"
    DRIVE_B="gatttool -b 00:07:80:7F:28:E1 -i hci0 --char-write --handle=0x0025 --value=0103"
    COAST_A="gatttool -b 00:07:80:7F:28:E1 -i hci0 --char-write --handle=0x0025 --value=01020000"
    COAST_B="gatttool -b 00:07:80:7F:28:E1 -i hci0 --char-write --handle=0x0025 --value=01030000"
    BREAK_A="gatttool -b 00:07:80:7F:28:E1 -i hci0 --char-write --handle=0x0025 --value=0002"
    BREAK_B="gatttool -b 00:07:80:7F:28:E1 -i hci0 --char-write --handle=0x0025 --value=0003"

    ### starts in Joystick mode ###
    control_by_JOYSTICK=True;

    num_axes=js.get_numaxes();
    num_buttons=js.get_numbuttons();
    num_hats=js.get_numhats();

    ### assuming 4 axes, 13 buttons and 1 hat

    flag=False;

    while True:
      x=y=motor_r=motor_l=0.0;
      event.pump();

      button_mode=js.get_button(B_SELECT);
      button_shot=js.get_button(B_SQUARE);

      if button_mode ==1:

        if control_by_JOYSTICK==True:
          control_by_JOYSTICK=False;
          print 'Control Mode=HAT';
        else:
          control_by_JOYSTICK=True;
          print 'Control Mode=JOYSTICK';


      ### joysticks axis [-1, +1]
      ### x=axis2 , y=-axis3
      ### ignore less than 0.2 (dead zone)
      ### apply log10(100x) (to reforce lower values)
      ### result is less or equal than 2 = log10(100)

      if control_by_JOYSTICK==True:

            # Control by Right Joystick, Axis 2 e 3

            axis2=js.get_axis(2);
            axis3=js.get_axis(3);

        if axis2>0:
          if axis2<0.2:
            x=0;
          else:
            x=log10(axis2*100);
        elif axis2<0:
          if axis2>-0.2:
            x=0;
          else:
            x=-log10(-axis2*100);
            else:
              x=0;

        if axis3>0:
          if axis3<0.2:
            y=0;
          else:
            y=-log10(axis3*100);
        elif axis3<0:
          if axis3>-0.2:
            y=0;
          else:
            y=log10(-axis3*100);
        else:
          y=0;

        if y<>0:
          if x<0:
            motor_r=100*y;
            # turn left => slow motor_l
               motor_l=y*(100+25*x);
          else:
            motor_el=100*y;
            # turn right => slow motor_r
            motor_r=y*(100-25*x);
        elif x<>0:
           # y=0, just turn
           motor_l=100*x;
           motor_r=-motor_l;

      else:

         # Control by HAT keys

         hat=js.get_hat(0);

         if hat==(0,1):
#            print 'FRONT';
            motor_r=100;
            motor_l=100;
          elif hat==(1,0):
#            print 'RIGHT';
            motor_l=100;
            motor_r=-100;
         elif hat==(0,-1):
#            print 'BACK';
            motor_r=-100;
            motor_l=-100;
         elif hat==(-1,0):
#            print 'LEFT';
            motor_l=-100;
            motor_r=100;
         elif hat==(1,1):
#            print 'FRONT+RIGHT';
            motor_l=100;
            motor_r=50;
         elif hat==(-1,1):
#            print 'FRONT+LEFT';
            motor_l=50;
            motor_r=100;
         elif hat==(-1,-1):
#            print 'BACK+LEFT';
            motor_l=-100;
            motor_r=-50;
         elif hat==(1,-1):
#            print 'BACK+RIGHT';
            motor_l=-50;
            motor_r=-100;


       # get direction and duty cycle

      if (motor_l<0):
     dir_l="00"
     duty_l=str(hex(int(-motor_l)))
      else:
         dir_l="01"
         duty_l=str(hex(int(motor_l)))

      if (motor_r<0):
     dir_r="01"
     duty_r=str(hex(int(-motor_r)))
      else:
         dir_r="00"
         duty_r=str(hex(int(motor_r)))

      # command+direction+dutycyle

      command_A=DRIVE_A+dir_r+duty_r[2:]
      command_B=DRIVE_B+dir_l+duty_l[2:]
      call(command_A, shell=True);
      call(command_B, shell=True);
      sleep(0.1)
#      call(BREAK_A,shell=True);
#      call(BREAK_B,shell=True);
      call(COAST_A,shell=True);
      call(COAST_B,shell=True);

    # end while

  except (KeyboardInterrupt, SystemExit):
      print "Exiting...";
  except Exception:
      traceback.print_exc(file=sys.stdout);

  js.quit();
  joystick.quit();
  display.quit();
  sys.exit(0);

if __name__ == "__main__":
    main()

 

 

SBrick + Snap!

A linguagem Snap! (antes designada BYOB) é uma linguagem de programação visual baseada na experiência “drag-and-drop”, uma reimplementação extendida da linguagem Scratch que tem ganho alguma notoriedade por ter sido incluída no ambiente gráfico dos Raspberry Pi.

Eis um exemplo de um programa muito simples, que faz um rover andar às voltas:

snap-SBrick1De há um par de anos para cá que alimento a ideia de utilizar Snap! para ensinar à miudagem [e não só] conceitos básicos de robótica. A forma como o Snap! está a ser desenvolvido permite criar «device extensions» para interagir com dispositivos físicos (descobri o Snap! justamente por ter uma extensão para Mindstorms NXT, a Snap-NXT by Technoboy10) e hoje criei a minha primeira extensão, para o Sbrick.

Basicamente editei a extensão para Mindstorms NXT, deitei fora a parte específica do NXT e acrescentei os meus comandos que invocam o comando gatttool do BlueZ (do que resulta a minha extensão apenas funcionar em Linux, lamento).

A device extension consiste em dois ficheiros:

O primeiro é um programa em Python que corre um servidor http muito básico que aceita comandos http. O segundo é uma definição XML das funcionalidades implementadas (dois comandos apenas: ‘move motor’ e ‘stop motor’, sendo possível controlar 3 «motores»: A = channel#1, B=channel#2 e P = channel#1+channel#2). Estando o programa ‘snap-sbrick.py’ a correr, lançamos o Snap! com a device extension quando acedemos por browser ao endereço:

http://snap.berkeley.edu/snapsource/snap.html#open:http://localhost:1330/snap-sbrick

Ou seja o Snap! em si é carregado da Universidade de Berkeley e apontado para o nosso PC de onde carrega as definições das funcionalidades adicionais. É possível carregar o Snap! a partir do nosso próprio PC para trabalhar em modo offline mas isso já é outra história.

A definição dos comandos pode também ser feita graficamente, sendo depois gerado um ficheiro XML semelhante ao acima referido:

  • comando ‘move motor’

snap-movemotor-def

  • comando ‘stop motor’

snap-stopmotor-defPodemos ver que a definição de ‘move motor’ está bem melhor que a de ‘stop motor’ – é feita uma validação dos parâmetros, se speed não pertencer ao intervalo [-100,100] ou se motor não for A/B/P é gerada uma excepção.

Relés USB

Adquiri um módulo USB-RLY16 que permite comandar 8 relés através de uma porta USB. A ideia original é controlar as minifiguras LEGO em palco pelo que com 8 relés poderei controlar 4 micromotores LEGO de forma independente e em ambos os sentidos (os Xutos&Pontapés são 5 pelo que 2 deles terão de partilhar o mesmo motor ou terei de arranjar outra forma para controlar um quinto motor).

O módulo é baseado num circuito FT232RL que emula uma porta série através da ligação USB. Em Linux o suporte a este tipo de devices já vem incluído no kernel sendo associado um device do tipo ‘/dev/ttyUSBn’. No meu PC com Ubuntu 14.04 funcionou à primeira, tal como no EV3 com ev3dev e estou 100% convencido que com Raspberry Pi e Raspbian acontecerá o mesmo.

São necessárias duas alimentações: uma para o accionamento dos relés e outra (ou outras) para os circuitos controlados pelos relés. Teoricamente podem ser as mesmas mas optei por separá-las:

  • uma pilha PP3 com ficha de 2.1mm para o accionamento dos relés
  • uma pilha PP3 em caixa LEGO para os motores

Nota: a pseudo-documentação afirma serem necessários pelo menos 12V/500 mA para o accionamento dos relés mas dei-me bem com os 9V de uma pilha alcalina PP3.

Ligando ao EV3:

$ dmesg
(...)
usb 1-1.4: new full-speed USB device number 6 using ohci
usbcore: registered new interface driver usbserial
usbcore: registered new interface driver ftdi_sio
usbserial: USB Serial support registered for FTDI USB Serial Device
ftdi_sio 1-1.4:1.0: FTDI USB Serial Device converter detected
usb 1-1.4: Detected FT232RL
usb 1-1.4: Number of endpoints 2
usb 1-1.4: Endpoint 1 MaxPacketSize 64
usb 1-1.4: Endpoint 2 MaxPacketSize 64
usb 1-1.4: Setting MaxPacketSize 64
usb 1-1.4: FTDI USB Serial Device converter now attached to ttyUSB0

Para comunicar com a placa é necessário configurar a porta série virtual (/dev/ttyUSB0) para comunicar a 19200 baud, 8 databits, 2 stop bits, sem paridade (19200 8N2) e sem hardware nem software handshaking. No Ubuntu usei o ‘puTTy’ mas como requer ambiente gráfico no ev3dev optei pelo ‘minicom’:

$ apt-get install minicom

O minicom tem um modo de setup:

$ minicom -s

depois de configurado (19200 8N2, no flow control) se carregarmos na tecla ‘d’ accionamos todos os relés e na tecla ‘n’ desactivamos todos (confirmem numa tabela ASCII que correspondem aos códigos decimais 100 e 110). Confirmado o funcionamento podemos sair (‘Control+A’ seguido de ‘Q’), a porta virtual permanece configurada até ao próximo reboot.

Podemos agora enviar comandos directamente da shell:

$ echo d > /dev/ttyUSB0
$ echo n > /dev/ttyUSB0

Em python podemos comunicar directamente com a porta virtual através da biblioteca pyserial (http://pyserial.sourceforge.net/):

$ apt-get install python-serial
import serial
from time import sleep

port = serial.Serial("/dev/ttyUSB0", baudrate=19200, bytesize=8, parity='N', stopbits=2, timeout=None, xonxoff=0, rtscts=0)
port.write("d");
sleep(1);
port.write("n");
sleep(1);
port.close()

O video acima corresponde ao script python abaixo:

import serial
from time import sleep


# da esquerda para a direita:
# motores A, B, C e D
#
# motor A e/f o/p
# motor B g/h q/r
# motor C i/j s/t
# motor D k/l u/v
#

def relays_all_off():
  port.write("n");

def relays_all_on():
  port.write("d");

### Motor A ###
def motor_A_Right():
  port.write("ep");

def motor_A_Left():
  port.write("of");

def motor_A_OFF():
  port.write("op");

### Motor B ###
def motor_B_Right():
  port.write("gr");

def motor_B_Left():
  port.write("qh");

def motor_B_OFF():
  port.write("qr");

### Motor C ###
def motor_C_Right():
  port.write("it");

def motor_C_Left():
  port.write("sj");

def motor_C_OFF():
  port.write("st");

### Motor D ###
def motor_D_Right():
  port.write("kv");

def motor_D_Left():
  port.write("ul");

def motor_D_OFF():
  port.write("uv");


port = serial.Serial("/dev/ttyUSB0", baudrate=19200, bytesize=8, parity='N', stopbits=2, timeout=None, xonxoff=0, rtscts=0)

motor_A_Left();
motor_B_Left();
motor_C_Left();
motor_D_Left();
sleep(1);
motor_A_OFF();
motor_B_OFF();
motor_C_OFF();
motor_D_OFF();
sleep(1);
motor_A_Right();
motor_B_Right();
motor_C_Right();
motor_D_Right();
sleep(1);
motor_A_OFF();
motor_B_OFF();
motor_C_OFF();
motor_D_OFF();
sleep(2);

motor_A_Left();
sleep(1);
motor_A_OFF();
sleep(1);
motor_B_Left();
sleep(1);
motor_B_OFF();
sleep(1);
motor_C_Left();
sleep(1);
motor_C_OFF();
sleep(1);
motor_D_Left();
sleep(1);
motor_D_OFF();
sleep(1);

port.close();

 

O meu próprio Media Center – Parte 2

This post is part 2 of 2 of  O meu próprio Media Center
Media Center: Diagrama de ligações

O centro das atenções prende-se no Media Center: é nele que estão armazenados todos os ficheiros de media (gravações de TDT, importações de CD e DVD, fotos, ficheiros avulsos).

O acesso aos conteúdo pode ser feito directamente pelo Media Center que tem um comando RF semelhante ao de uma TV (embora possa ser comandado de outras formas como por browser ou por aplicação Android) e está ligado por um cabo HDMI à TV pelo que imagem e som saem directamente por ela. Uma desvantagem desta nova versão do nosso Media Center é a TV ter apenas som estereo e tanto o MC como a TV apenas terem ligações S/PDIF ópticas (Toslink) – nas versões anteriores o som em vez de ser encaminhado para a TV era encaminhado por uma ligação S/PDIF analógica (RCA) para um sistema 5.1 que se quiser manter terei primeiro de encontrar um adaptador RCA->Toslink.

É ainda possível aceder de outras formas aos conteúdos do MC graças às ligações de rede:

  • através de um browser
  • através de um cliente UPnP/DLNA às gravações, vídeos, fotos e músicas; a própria TV já tem um cliente DLNA mas podemos usar aplicações como o Windows Media Player ou o VLC a partir de PC, Tablet e mesmo alguns telemóveis
  • através de um frontend MythTV (o do próprio projecto ou outro como o XBMC) a tudo, inclusive Live TV

No próximo artigo desta série abordo a configuração de hardware do Media Center.