Módulo MFL em Tomar

Os meus primeiros 3 módulos MFL (Módulo Ferrovia LEGO) junto dos restantes módulos da PLUG no Tomar BRInCKa 2015:

O  módulo ao centro tem dois desvios motorizados, bem como um «desengatador» no troço de recta entre as duas agulhas. Toda a electrónica está disfarçada por baixo das linhas.

O módulo da direita consiste numa plataforma rotativa motorizada e controlada por um LEGO Mindstorms EV3 correndo ev3dev (uma variante de Debian para o EV3). Sobre o carril um pequeno comboio controlado por SBrick (bluetooth).

O módulo da esquerda, quase 100% da minha marida, começou com uma pequena cascata no nosso presépio do Natal anterior.

A montagem no BRInCKa 2015 não ficou completa por isso todas as demonstrações são manuais, controladas por mim. Neste video gravado em casa é o computador (LEGO Mindstorms EV3) a fazer tudo sozinho:

O que faltou montar e que permite a automação plena? As componentes RFID – por baixo do pequeno comboio há um identificador RFID do tamanho de uma moeda que permite identificá-lo e em certa medida seguir-lhe a posição. Na montagem do video existem dois leitores RFID (USB) por baixo da linha (um no extremo direito e outro junto ao sistema de desengate). O EV3 utiliza esses sensores para perceber se o comboio está nos sítios certos antes de passar à acção seguinte.

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)