AD4M4ST0R – medindo luminosidade

This post is part 6 of 9 of  AD4M4ST0R - um rover LEGO

A ideia é utilizar um sensor de cor para medir a luminosidade de modo a acender os LEDs de iluminação apenas o necessário para garantir uma imagem razoável.

O sensor utilizado é um EV3 Color Sensor (45506). Na wiki do projecto ev3dev há 2 artigos com a informação suficiente:

Após a ligação o sensor é imediatamente reconhecido:

root@ev3dev:~# dmesg
(...)
ev3-uart-host in2:ev3-uart-host: Started.
Registered LEGOEV3 line discipline. (29)
msensor sensor0: Mindstorms sensor registered.

Sendo o único sensor ligado aparece referenciado como ‘sensor0’ debaixo da classe ‘msensor’:

root@ev3dev:~# ls /sys/class/msensor/ -la
lrwxrwxrwx  1 root root 0 Aug 24 00:46 sensor0 -> ../../devices/platform/serial8250.0/tty/ttyS0/ttyS0:ev3-uart-sensor/msensor/sensor0

Temos a seguinte informação disponível:

root@ev3dev:~# ls /sys/class/msensor/sensor0
bin_data     device  mode    num_values  power      type_id    units    value1    value3    value5    value7
bin_data_format  dp     modes    port_name   subsystem  uevent    value0    value2    value4    value6

O sensor é inicializado no modo ‘COL-REFLECT’ mas podemos mudar para um dos seguintes modos disponíveis:

root@ev3dev:~# cat /sys/class/msensor/sensor0/mode
COL-REFLECT

root@ev3dev:~# cat /sys/class/msensor/sensor0/modes
COL-REFLECT COL-AMBIENT COL-COLOR REF-RAW RGB-RAW COL-CAL

Pretendo operar no modo ‘COL-AMBIENT’:

root@ev3dev:~# echo COL-AMBIENT > /sys/class/msensor/sensor0/mode

Neste modo o sensor gera apenas um valor:

root@ev3dev:~# cat /sys/class/msensor/sensor0/num_values
1

que será portanto ‘value0’:

root@ev3dev:~# cat /sys/class/msensor/sensor0/value0
3

Infelizmente a gama de valores lida pelo sensor deixa muito a desejar: no mesmo local na minha sala observei de ‘1’ (de noite com luz fraca) a ’17’ (numa tarde típica de Agosto sendo a sala virada a Sul embora não haja incidência directa no sensor). E com uma lanterna a apontar directamente ao sensor obtenho ’68’.

Tirei algumas fotos com vários valores de leitura e vários duty cycles aplicados a 3 pares de LEDs Power Functions para tentar elaborar uma regra (o rover e a minifig sempre no mesmo local com cerca de 15 cm entre a webcam e a minifig, a iluminação dos LEDs nunca alterou o valor medido pelo sensor e todas as fotos foram apenas reduzidas dos 640×480 originais para 160×120)

Brightness Duty Cycle
(%) 0% 25% 33% 50% 66% 75% 100%
1 bright01-duty000 bright01-duty100
3 bright03-duty000 bright03-duty025 bright03-duty033 bright03-duty050 bright03-duty066 bright03-duty075 bright03-duty100
4 bright04-duty000 bright04-duty025 bright04-duty033 bright04-duty050 bright04-duty066 bright04-duty075 bright04-duty100
5 bright05-duty000 bright05-duty025 bright05-duty033 bright05-duty050 bright05-duty066 bright05-duty075 bright05-duty100
6 bright06-duty000 bright06-duty025 bright06-duty033 bright06-duty050 bright06-duty066 bright06-duty075 bright06-duty100
13 bright13-duty000 bright13-duty033 bright13-duty066 bright13-duty100
17 bright17-duty000 bright17-duty066 bright17-duty066 bright17-duty100

 

Com esta informação optei pela seguinte função ‘fuzzy logic’:

  • Brightness <6  => Duty Cycle = 100%
  • Brightness ∈[6, 7] => Duty Cycle = 75%
  • Brightness ∈[8, 9] => Duty Cycle = 66%
  • Brightness ∈[10,11] => Duty Cycle = 50%
  • Brightness ∈[12,13] => Duty Cycle = 33%
  • Brightness >13 => Duty Cycle = 0%

AD4M4ST0R – controlando LEDs

This post is part 5 of 9 of  AD4M4ST0R - um rover LEGO

A qualidade das fotos tiradas pela webcam Logitech C170 não é má… desde que haja suficiente luz ambiente. Por isso lembrei-me de ligar LED’s Power Functions a uma das portas de saída ainda disponíveis para funcionar como «flashes» ao tirar a fotografia.

Infelizmente a versão actual do ev3dev ainda não permite o controlo individual das portas (foi aberto hoje um issue, depois de uma troca de ideias) por isso a única maneira é enganar o ev3dev fazendo-o acreditar que tem um motor.

Os motores e os sensores EV3 têm uma funcionalidade «Auto-ID» que permite ao EV3 identificar que dispositivo foi ligado a cada porta (género “Plug and Play”). Nos videos do utilizador “TecnicRobot” ele utiliza um par de resistências para que os seus dispositivos de saída pareçam ser motores EV3.

Para isso fiz um cabo semelhante a este (não tinha resistências de 1KΩ e 10 KΩ à mão mas como o que interessa é a proporção usei de 2.2KΩ e 22KΩ) e funciona.

(o pinout do cabo EV3 foi baseado neste)

    EV3 Cable                             PF Cable
                                           - 9V
White    Pin 1 (M1)     -------------------- C2 (middle)
Black    Pin 2 (M2)     -------------------- C1 (middle)
Red      Pin 3 (GND)    ----[R1]---x       - GND
Green    Pin 4 (POWER)  ----[R2]---x
Yellow   Pin 5 (TACHO0) -----------x
Blue     Pin 6 (TACHO1) -------- Pin 3 (GND)

R2 ~10xR1
R1 = 1K, 2K2, 3K3...
R2 = 10K, 22K, 33K...
dmesg:
tacho-motor tacho-motor2: Tacho motor registered.
ev3-tacho-motor outC:motor: A Tacho Motor connected to port outC gpio 93 irq 194

Para acender os LEDs a meia intensidade:

echo 50 > /sys/class/tacho-motor/tacho-motor2/duty_cycle_sp
echo 1 > /sys/class/tacho-motor/tacho-motor2/run

Para acender na máxima intensidade:

echo 100 > /sys/class/tacho-motor/tacho-motor2/duty_cycle_sp
echo 1 > /sys/class/tacho-motor/tacho-motor2/run

ad4m4st0r-02 (parece-me que é altura de fazer um cabo decente e dar uma arrumação aos cabos do AD4M4ST0R).

Durante a noite, numa sala com pouca iluminação (halogéneo)

ad4m4st0r-03Durante a manhã, no mesmo local com iluminação média (natural mas indirecta):

ad4m4st0r-04A terceira imagem foi tirada com um segundo par de LED’s Power Functions o que me levou na noite seguinte a acrescentar um terceiro par:

ad4m4st0r-06As 4 imagens correspondem a Duty Cycles de 0%, 33%, 67% e 100% (ou seja os LED’s apagados, o equivalente a um par aceso, o equivalente a dois pares acesos e os três pares acesos).

Assim que me for possível vou tentar medir a luminosidade ambiente com um EV3 Color Sensor (45506) para determinar automaticamente o Duty Cycle a aplicar aos LEDs antes de cada fotografia.

AD4M4ST0R – o código

This post is part 4 of 9 of  AD4M4ST0R - um rover LEGO

Para terminar a apresentação do AD4M4ST0R faltava apenas o código.

O programa principal é um script escrito em python ‘joy.py’ que lida com o gamepad e os motores. O controlo da webcam e do buzzer é delegado em dois bash scripts (‘saycheese.sh’ e ‘beep.sh’).

O programa principal, “joy.py’:

import sys, traceback, os
os.environ['SDL_VIDEODRIVER'] = 'dummy'

# from math import sqrt
from math import log10
from subprocess import call
from time import sleep
from pygame import joystick, event, display

### botoes ###
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();

    ### arranca em modo Joystick ###
    control_by_JOY=True;

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

    ### sabemos a priori que tem 4 axes, 13 buttons e 1 hat

    ### inicializa motores com Duty Cycle=0
        call("echo 0 > /sys/class/tacho-motor/tacho-motor0/duty_cycle_sp", shell=True);
        call("echo 0 > /sys/class/tacho-motor/tacho-motor1/duty_cycle_sp", shell=True);
        call("echo 1 > /sys/class/tacho-motor/tacho-motor0/run", shell=True);
        call("echo 1 > /sys/class/tacho-motor/tacho-motor1/run", shell=True);

    flag=False;

    while True:
      x=y=motor_direito=motor_esquerdo=0.0;
      event.pump();

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

      if button_mode ==1:

        call("./beep.sh" , shell=True);

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

      if button_shot==1:
        print 'Cheese!!!';
        call("/root/saycheese.sh", shell=True);


      ### eixo dos joysticks entre -1 e +1
        ### x=axis2 , y=-axis3
      ### ignorar valores abaixo de 0.1 (dead zone)
      ### aplicar log10(100x) aos restantes (para dar enfase aos mais baixos)
      ### o resultado e' 0 ou 1 a 2 por isso
      ###    multiplicar por 25 da' valores Duty Cycle nulos ou entre 25 e 50

      if control_by_JOY==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.1:
            x=0;
          else:
            x=log10(axis2*100);
        elif axis2<0:
          if axis2>-0.1:
            x=0;
          else:
            x=-log10(-axis2*100);

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

        if y<>0:
          if x<0:
            motor_direito=50*y/2;
            # rodar para a esquerda => reduzir motor esquerdo
               motor_esquerdo=motor_direito*(1+x/3);
          else:
            motor_esquerdo=50*y/2;
            # rodar para a direita => reduzir motor direito
            motor_direito=motor_esquerdo*(1-x/3);
        elif x<>0:
           # y=0, apenas roda
           motor_esquerdo=25*x;
           motor_direito=-motor_esquerdo;

        else:

         # Control by HAT keys

         hat=js.get_hat(0);

         if hat==(0,1):
            ### anda em frente ###
            motor_direito=50;
            motor_esquerdo=50;
          elif hat==(1,0):
            ### roda para a direita ###
            motor_esquerdo=50;
            motor_direito=-50;
         elif hat==(0,-1):
            ### recua ###
            motor_direito=-50;
            motor_esquerdo=-50;
         elif hat==(-1,0):
            ### roda para a esquerda ###
            motor_esquerdo=-50;
            motor_direito=50;
         elif hat==(1,1):
            ### frente e roda para a direita ###
            motor_esquerdo=50;
            motor_direito=25;
         elif hat==(-1,1):
            ### frente e roda para a esquerda ###
            motor_esquerdo=25;
            motor_direito=50;
         elif hat==(-1,-1):
            ### recua e roda para a esquerda ###
            motor_esquerdo=-50;
            motor_direito=-25;
         elif hat==(1,-1):
            ### recua e roda para a direita ###
            motor_esquerdo=-25;
            motor_direito=-50;

       comando_motordir = "echo " + repr(motor_direito) + " > /sys/class/tacho-motor/tacho-motor0/duty_cycle_sp";
       comando_motoresq = "echo " + repr(motor_esquerdo) + " > /sys/class/tacho-motor/tacho-motor1/duty_cycle_sp";

       call(comando_motordir, shell=True);
       call(comando_motoresq, shell=True);

    # end while

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

    call("echo 0 > /sys/class/tacho-motor/tacho-motor0/duty_cycle_sp", shell=True);
    call("echo 0 > /sys/class/tacho-motor/tacho-motor1/duty_cycle_sp", shell=True);
    call("echo 0 > /sys/class/tacho-motor/tacho-motor0/run", shell=True);
    call("echo 0 > /sys/class/tacho-motor/tacho-motor1/run", shell=True);

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

if __name__ == "__main__":
    main()

O controlo da webcam, ‘saycheese.sh’:

#!/bin/sh
filename=$(date +"%d-%m-%y_%Hh%Mm%Ss")
fswebcam -d /dev/video0 -p MJPEG -r 640x480 -q -D 1 -S 3 -s brightness=10 -s contrast=20  $filename.jpg

O controlo do buzzer, ‘beep.sh’:

#!/bin/bash
echo -en "07" > /dev/tty1

A quem o considerar útil, esteja à vontade para utilizar / feel free to use as you wish.

Computer Vision com LEGO

Com a webcam a funcionar e com tanta coisa em comum com a comunidade Raspberry Pi ocorreu-me experimentar o OpenCV e testar as capacidades do EV3  no campo da Computer Vision.

E afinal nem é muito difícil começar: segundo o artigo ‘Face Recognition With Python, in Under 25 Lines of Code‘ são mesmo suficientes menos de 25 linhas de código em Python.

Em vez de instalar o opencv todo, instalei apenas o módulo para python:

root@ev3dev:~# apt-get install python-opencv

Depois foi só criar o script ‘teste-opencv.py’:

import numpy as np
import cv2

imagePath = "/root/teste-face.jpg"
cascPath = "/root/haarcascade_frontalface_default.xml"
resultPath ="/root/resultado.jpg"

faceCascade = cv2.CascadeClassifier(cascPath)

image = cv2.imread(imagePath)
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)

faces = faceCascade.detectMultiScale(
    gray,
    scaleFactor=1.1,
    minNeighbors=5,
    minSize=(30, 30),
    flags = cv2.cv.CV_HAAR_SCALE_IMAGE
)

print "Found {0} faces!".format(len(faces))

# Draw a rectangle around the faces
for (x, y, w, h) in faces:
    cv2.rectangle(image, (x, y), (x+w, y+h), (0, 255, 0), 2)

cv2.imwrite(resultPath,image)

Como não instalei o opencv não tenho os ficheiros xml com regras de classificação. Para este exemplo o autor do artigo referido disponibiliza também o ficheiro ‘haarcascade_frontalface_default.xml‘.

Para esta foto ‘teste-face.jpg’:

teste-face02O resultado do script, ao fim de 3 a 5 minutos de CPU a 80% foi este:

resultado02Abre um mundo de possibilidades, não abre?

ev3dev – webcam

This post is part 2 of 9 of  AD4M4ST0R - um rover LEGO

A webcam USB utilizada no AD4M4ST0R é uma Logitech C170.

Depois de ligada são reconhecidos dois devices: uma webcam propriamente dita do tipo UVC (USB Video Class) gerida pelo v4l2 e um microfone gerido como uma placa de som pelo ALSA:

root@ev3dev:~# dmesg
(...)
usb 1-1.3: new full-speed USB device number 5 using ohci
Linux video capture interface: v2.00
uvcvideo: Found UVC 1.00 device Webcam C170 (046d:082b)
input: Webcam C170 as /devices/platform/ohci.0/usb1/1-1/1-1.3/1-1.3:1.0/input/input3
usbcore: registered new interface driver uvcvideo
USB Video Class driver (1.1.1)
usbcore: registered new interface driver snd-usb-audio

O microfone não aparece como dispositivo USB, apenas a webcam:

root@ev3dev:~# lsusb
Bus 001 Device 005: ID 046d:082b Logitech, Inc.
(...)

mas se formos ver ao alsamixer  temos lá uma nova placa de som: “Webcam C170”.

A parte mais crítica, a criação de um dispositivo video, também funcionou:

root@ev3dev:~# ls /dev

mostra entre outros “video0”.

Como a webcam foi também reconhecida como um dispositivo OHCI temos ainda:

root@ev3dev:~# ls /dev/input/by-id/ -la
(...)
lrwxrwxrwx 1 root root   9 Aug 18 12:13 usb-_Webcam_C170-event-if00 -> ../event3
(...)

 

Para capturar imagens da webcam optei pelo fswebcam

apt-get install fswebcam

Como já o tinha experimentado com esta mesma webcam no meu Ubuntu, avanço para um comando que sei que funciona:

fswebcam -d /dev/video0 -p YUYV -r 640x480 teste.jpg

E efectivamente funciona só que quando fui ver a imagem esta tinha apenas 176×144 e não 640×480 (e o próprio comando devolve isso: “Adjusting resolution from 640×480 to 176×144”). Mas que raio…?

Perdi algum tempo nisto por isso vou alongar-me um pouco mais para que outros que passem pelo mesmo possam beneficiar também.

Descobri que a câmara permite despejar video em dois formatos diferentes: YUYV e MJPG:

root@ev3dev:~# v4l2-ctl --list-formats
ioctl: VIDIOC_ENUM_FMT
    Index       : 0
    Type        : Video Capture
    Pixel Format: 'YUYV'
    Name        : YUV 4:2:2 (YUYV)

    Index       : 1
    Type        : Video Capture
    Pixel Format: 'MJPG' (compressed)
    Name        : MJPEG

MJPG (Motion JPEG) é um formato com compressão (e alguma perda também) e por isso consome menos largura de banda que o YUYV. A minha teoria é de que sendo o bus USB do EV3 apenas 1.1 (11 Mbps) em vez de USB 2.0 (480 Mbps) como no meu Ubuntu a webcam de algum modo se autoconfigura para a largura de banda disponível. Por isso no Ubuntu ao pedir com o fswebcam uma foto 640×480 extraída de video gerado em formato YUV ele consiga mas no EV3 seja obrigado a reduzir para 176×144.

A solução foi indicar ao fswebcam para lidar com MJPG:

fswebcam -d /dev/video0 -p MJPEG -r 640x480 teste.jpg

Agora já funciona, excepto que nalgumas situações ocorre um erro semelhante a este (os dois números hexadecimais do final variam):

GD Error: gd-jpeg: JPEG library reports unrecoverable error: Not a JPEG file: starts with 0x92 0x7

Aparentemente algumas webcams (e pelos vistos a maioria são da Logitech?) demora a estabilizar a captura e por isso produz lixo nas primeiras frames. As soluções passam invariavelmente por descartar umas quantas frames inicais… só ainda não encontrem o número mágico:

fswebcam -d /dev/video0 -p MJPEG -r 640x480 -S 20 teste.jpg

Descartar 20 frames funciona sempre mas parece-me um exagero. Além disso descobri que à medida que se vai utilizando podemos baixar o número, até mesmo deixar de descartar qualquer frame… Parece que a câmara “aquece” ou se vai auto-ajustando, será?

Para facilitar a utilização a partir de outros programas criei um script ‘saycheese.sh’ que ao ser invocado tira uma fotografia e guarda-a com um nome correspondendo à data:

#!/bin/sh
 filename=$(date +"%d-%m-%y_%Hh%Mm%Ss")
 fswebcam -d /dev/video0 -p MJPEG -r 640x480 -q -D 1 -S 10 -s brightness=8 -s contrast=10  $filename.jpg

Utilizo alguns outros parâmetros:

  • “-D 1” força um atraso de 1 segundo entre a activação da câmara e o íncio da captura
  • -s brightness=8 aumenta ligeiramente o brilho
  • -s contrast=10 diminui ligeiramente o contraste

Os valores de brightness e contrast, bem como outros aceites pela webcam, podem ser questionados à própria câmara:

root@ev3dev:~# v4l2-ctl --all
Driver Info (not using libv4l2):
    Driver name   : uvcvideo
    Card type     : Webcam C170
    Bus info      : usb-ohci.0-1.3
    Driver version: 3.14.7
    Capabilities  : 0x84000001
        Video Capture
        Streaming
        Device Capabilities
    Device Caps   : 0x04000001
        Video Capture
        Streaming
Priority: 2
Video input : 0 (Camera 1: ok)
Format Video Capture:
    Width/Height  : 640/480
    Pixel Format  : 'MJPG'
    Field         : None
    Bytes per Line: 0
    Size Image    : 921600
    Colorspace    : SRGB
Crop Capability Video Capture:
    Bounds      : Left 0, Top 0, Width 640, Height 480
    Default     : Left 0, Top 0, Width 640, Height 480
    Pixel Aspect: 1/1
Streaming Parameters Video Capture:
    Capabilities     : timeperframe
    Frames per second: 30.000 (30/1)
    Read buffers     : 0
                     brightness (int)    : min=-64 max=64 step=1 default=0 value=0
                       contrast (int)    : min=0 max=30 step=1 default=13 value=13
                     saturation (int)    : min=0 max=127 step=1 default=38 value=38
                            hue (int)    : min=-16000 max=16000 step=1 default=0 value=0
 white_balance_temperature_auto (bool)   : default=1 value=1
                          gamma (int)    : min=20 max=250 step=1 default=100 value=100
           power_line_frequency (menu)   : min=0 max=2 default=1 value=1
      white_balance_temperature (int)    : min=2800 max=6500 step=1 default=5000 value=5000 flags=inactive
                      sharpness (int)    : min=0 max=100 step=1 default=35 value=35
         backlight_compensation (int)    : min=0 max=1 step=1 default=0 value=0
                  exposure_auto (menu)   : min=0 max=3 default=3 value=3
              exposure_absolute (int)    : min=2 max=5000 step=1 default=312 value=10000 flags=inactive
         exposure_auto_priority (bool)   : default=0 value=1
                   pan_absolute (int)    : min=-72000 max=72000 step=3600 default=0 value=0
                  tilt_absolute (int)    : min=-54000 max=54000 step=3600 default=0 value=0
                  zoom_absolute (int)    : min=1 max=2 step=1 default=1 value=1

(embora nem todos os comandos aceites pela câmara sejam perceptíveis, só experimentando vários valores para ver a diferença)

AD4M4ST0R – um rover LEGO

This post is part 1 of 9 of  AD4M4ST0R - um rover LEGO

Na continuação das experiências com o ev3dev, dois dispositivos USB um pouco mais complicados:

  • wireless gamepad
  • webcam

Desta vez mostro primeiro o resultado e preocupo-me depois com as explicações 🙂

ad4m4st0r-01

O gamepad é um gamepad genérico para Sony Playstation 3 e PC, penso que a maioria dos gamepads USB serão igualmente reconhecidos. Já com as webcams não será bem assim, será necessário compatibilidade com v4l2 (webcams) ou gPhoto2 (máquinas fotográficas). Neste exemplo estou a usar uma webcam Logitech C170 que funciona com o v4l2 através do uvcvideo. Também utilizei com sucesso uma Canon Digital IXUS 500 com o gPhoto2. E também tentei com 3 webcams antigas sem sucesso (apesar de 2 delas funcionarem no meu Ubuntu… pelos vistos funcionam em modo de compatibilidade v4l1 que não parece existir no ev3dev).

A foto abaixo (não tocada) foi tirada pelo AD4M4ST0R durante o decorrer do video anterior:19-08-14_09h53m54s

[Actualizado a 20 de Agosto]

Para quem estiver interessado, publiquei em artigos distintos os detalhes quanto:

ev3dev – automatic login

Depois do ev3dev terminar a sequência de boot, a consola do EV3 saúda-nos com uma janela de login. Quando queremos utilizar em pleno a consola é necessário “desbloqueá-la” e para isso tive de descobrir como implementar autologin.

Encontrei várias referências a usar o ‘/etc/inittab’ mas não consegui resultados práticos. Encontrei contudo outra abordagem que resultou:

mkdir /etc/systemd/system/getty@tty1.service.d/
nano /etc/systemd/system/getty@tty1.service.d/autologin.conf

No ficheiro autologin.conf bastou colocar estas 3 linhas:

[Service]
ExecStart=
ExecStart=-/usr/bin/agetty --autologin root --noclear %I 38400 linux

Após reboot o ev3dev fez o login automático com o user root, como pretendido.

ev3dev – keypad USB

Ter o LEGO Mindstorms EV3 a correr uma versão quase standard de Debian Linux começa agora a revelar as suas vantagens: podemos atirar-lhe com quase tudo o que tivermos na nossa gaveta de sucata… como por exemplo um teclado numérico USB:

USB Keypad (roline)

O sistema operativo reconhece-o como um teclado convencional:

root@ev3dev:~# dmesg
usb 1-1.4.1: new low-speed USB device number 7 using ohci
input: USB-compliant keyboard as /devices/platform/ohci.0/usb1/1-1/1-1.4/1-1.4.1/1-1.4.1:1.0/0003:0B38:0003.0001/input/input2
hid-generic 0003:0B38:0003.0001: input: USB HID v1.10 Keyboard [USB-compliant keyboard] on usb-ohci.0-1.4.1/input0
input: USB-compliant keyboard as /devices/platform/ohci.0/usb1/1-1/1-1.4/1-1.4.1/1-1.4.1:1.1/0003:0B38:0003.0002/input/input3
hid-generic 0003:0B38:0003.0002: input: USB HID v1.10 Mouse [USB-compliant keyboard] on usb-ohci.0-1.4.1/input1
root@ev3dev:~# lsusb
Bus 001 Device 007: ID 0b38:0003 Gear Head Keyboard
Bus 001 Device 006: ID 058f:9254 Alcor Micro Corp. Hub

(o teclado tem embutido um hub USB de 2 portas daí o segundo device USB reportado)

O sistema operativo cria entradas do tipo ‘event#’ em ‘/dev/input’ assim como apontadores com descritivos mais legíveis:

ls /dev/input/by-id/ -la
lrwxrwxrwx 1 root root  10 Aug 13 23:38 usb-0b38_USB-compliant_keyboard-event-if01 -> ../event2
lrwxrwxrwx 1 root root  10 Aug 13 23:38 usb-0b38_USB-compliant_keyboard-event-kbd -> ../event1

De modo que o teclado pode ser referido por um destes dois ficheiros:

  • /dev/input/event1
  • /dev/input/usb-0b38_USB-compliant_keyboard-event-kbd

Enquanto que o sufixo “event1” é variável, o descritivo “by-id” não é – no meu laptop Ubuntu o mesmo comando retorna:

~$ ls /dev/input/by-id/ -la
lrwxrwxrwx 1 root root  10 Aug 13 23:38 usb-0b38_USB-compliant_keyboard-event-if01 -> ../event19
lrwxrwxrwx 1 root root  10 Aug 13 23:38 usb-0b38_USB-compliant_keyboard-event-kbd -> ../event18

por isso é preferível utilizar o descritivo “by-id”.

Não é suficiente ler os valores deste ficheiro – o primir de uma tecla gera gera um código de vários caracteres aparentemente ininteligiveis:

~$ cat /dev/input/event1
w��S�VSw��S�VEw��S�Vw��S�Ww��S�Ww��S��Sw��S��Ew��S��^C

A solução é usar o comando ‘showkey -s’ para descodificar. Por exemplo premindo a tecla ‘4’ com e sem ‘NumLock’ activo resulta:

cat /dev/input/by-id/usb-0b38_USB-compliant_keyboard-event-kbd > showkey -s
  • Sem Numlock: “^[[D”
  • Com NumLock: “4”

Felizmente isto só é necessário se quisermos ler directamente o teclado. Em situações normais, quando os nossos programas estão a correr, o próprio sistema operativo pode fazer esse trabalho por nós. No caso do ev3dev podemos ler uma única tecla a partir da consola com o seguinte comando bash:

read -s -n 1 Tecla < /dev/tty1

O parâmetro ‘-s’ serve para correr em modo silencioso (i.e. a tecla não é simultâneamente escrita na consola) e o parâmetro ‘-n 1’ força a leitura de um único caracter (quanto ao pipe ‘< /dev/tty1’ descobri entretanto que tanto faz referir /dev/tty0 como /dev/tty1 portanto a partir daqui refiro a consola do ev3dev como /dev/tty1… porque sim)

O script abaixo junta esta informação com a obtida anterior a respeito dos motores e permite controlar a rotação do motor com as teclas ‘4’ (LEFT) e ‘6’ (RIGHT), terminado com a tecla ‘ENTER’.

#!/bin/bash

#chmod +x controlkey.sh

tput clear > /dev/tty1
setfont /usr/share/consolefonts/Lat15-TerminusBold32x16.psf.gz
echo -n "Prima tecla" > /dev/tty1

while true; do

  read -s -n 1 char < /dev/tty1
  if [[ "$char" = "" ]]; then
    break
  fi

  case "$char" in

  4) echo "LEFT" > /dev/tty1

     echo   0 > /sys/class/tacho-motor/tacho-motor0/duty_cycle_sp
     echo   1 > /sys/class/tacho-motor/tacho-motor0/run
     echo   50 > /sys/class/tacho-motor/tacho-motor0/duty_cycle_sp
     sleep 1s
     echo   0 > /sys/class/tacho-motor/tacho-motor0/run
     echo   0 > /sys/class/tacho-motor/tacho-motor0/duty_cycle_sp

    ;;

  6) echo "RIGHT" > /dev/tty1

     echo   0 > /sys/class/tacho-motor/tacho-motor0/duty_cycle_sp
     echo   1 > /sys/class/tacho-motor/tacho-motor0/run
     echo   -50 > /sys/class/tacho-motor/tacho-motor0/duty_cycle_sp
     sleep 1s
     echo   0 > /sys/class/tacho-motor/tacho-motor0/run
     echo   0 > /sys/class/tacho-motor/tacho-motor0/duty_cycle_sp

    ;;
   esac

done

echo "Bye!" > /dev/tty1

 

 

ev3dev – backup e boot time

Nesta primeira semana de experiências com o ev3dev utilizei sempre o mesmo cartão microSD (um Kingston microSD 2GB penso que classe 4). Os tempos de arranque (desde o carregar no botão de power até ao primeiro ‘ping’) foram sempre de 60 segundos, não é muito mau comparado com o tempo de arranque do EV3 com o firmware LEGO por isso não me preocupei em experimentar outros cartões.

Mas hoje lembrei-me de ter visto alguma utilização de swap e já que as operações de update são longas (mais de 10 minutos só para actualizar o catálogo com ‘apt-get update’) podia ser que a coisa melhorasse com um cartão mais rápido.

Trocar de cartão é simples e faz-se em 5 minutos:

  1. colocar o cartão no meu portátil Ubuntu
  2. criar uma imagem do cartão e guardar como backup
  3. tirar o cartão e colocar outro qualquer
  4. repor a imagem anterior neste novo cartão

Como novo cartão utilizei um SanDisk Ultra de 4 GB (microSDHC classe UHS-I).

Os tempos de arranque desceram de 60 segundos para 50 segundos, nada mau. E a actualização do catálogo (‘apt-get update’) passou para ligeiramente menos de 8 minutos. Não é um ganho espantoso mas se estiver a trabalhar com pilhas ou baterias em vez de regulador DC já é qualquer coisa.

Como o novo cartão é de 4 GB em vez de 2 GB posso aproveitar o novo espaço extendendo o file system. Podia tê-lo feito de dentro do próprio ev3dev mas ainda não domino muito a linha de comando por isso fi-lo em 3 fases:

  1. Colocando o cartão no meu Ubuntu e utilizando o Gparted para criar uma nova partição do tipo ‘lvm2 pv’ com os 1906 MB livres no cartão
  2. Ainda no Ubuntu usando o system-config-lvm para adicionar a nova partição ao Volume Group já existente
  3. Voltando ao ev3dev e extendendo o Logical Volume ‘root’:
root@ev3dev:~# lvextend --extents +100%FREE /dev/ev3devVG/root
  Extending logical volume root to 3.59 GiB
  Logical volume root successfully resized

[editado posteriormente]

faltou indicar como expandir o file system:

resize2fs  /dev/ev3devVG/root

[se não der  no EV3 pode-se fazer no Ubuntu, precedendo o comando com ‘sudo’]

 

EV3 – orelha para micro-SD

Esta é uma das dicas mais simples e contudo mais úteis que vi nos últimos tempos:

EV3: Adding a pull-tab to your micro-SD card

Felizmente tenho um canivete-suiço de carteira com uma pinça que me permitiu desenrascar-me da primeira vez que precisei retirar o cartão (“que raio… mas isto não sai?”). Mas não torna a acontecer:

ev3-microsd-orelha01ev3-microsd-orelha02A LEGO só deve ter dado EV3 experimentais a malta com unhas muito grandes…