LEGO officially uses ev3dev

Yesterday LEGO Education quietly released “Python for EV3“:

You can now use your EV3 Brick to unleash the power of Python programming using MicroPython. Simply install the EV3 MicroPython image onto any micro SD card and boot up your EV3 Brick from it to start programming straight away.

What it really is: a full ev3dev image with a micropython environment meant to be used from Visual Studio Code through an EV3 MicroPython extension.

Amazing work from David Lechner, Laurens Valk, and Anton Vanhoucke, built over the shoulders of lots of other giants. Congratulations to all!

The documentation states that it uses a ‘pybricks-micropython’ environment and new ‘pybrick’ library, not yet available outside of this image but that’s just a matter of time.

Micropython programs tend to use less resources than common python and also start much faster. The ev3dev-lang-python is still included on the image but for simple projects this new micropython environment will be of great use for people starting with EV3 and text-oriented languages.

The image is really a full ev3dev stretch-based image, the ‘robot’ user is still available (password is “maker”) so we can still access through SSH and use it the way we were used:

ssh robot@ev3dev.local
Password: 
Linux ev3dev 4.14.96-ev3dev-2.3.2-ev3 #1 PREEMPT Sun Jan 27 21:27:35 CST 2019 armv5tejl
             _____     _
   _____   _|___ /  __| | _____   __
  / _ \ \ / / |_ \ / _` |/ _ \ \ / /
 |  __/\ V / ___) | (_| |  __/\ V /
  \___| \_/ |____/ \__,_|\___| \_/

Debian stretch on LEGO MINDSTORMS EV3!

Kernel is very recent but there is already a newer version available – since LEGO keeped the link to ev3dev repositories so the usual ‘sudo apt update’ and ‘sudo apt upgrade’ works:

The following NEW packages will be installed:
linux-image-4.14.111-ev3dev-2.3.3-ev3 rtl8188eu-modules-4.14.111-ev3dev-2.3.3-ev3
rtl8812au-modules-4.14.111-ev3dev-2.3.3-ev3
The following packages will be upgraded:
jri-11-ev3 libnss-myhostname libnss-resolve libpam-systemd libsmbclient libsystemd0 libudev1 libwbclient0
linux-image-ev3dev-ev3 samba-libs systemd systemd-sysv udev wget wpasupplicant

By the way, micropython says:

robot@ev3dev:~$ micropython 
MicroPython v1.9.4 on 2018-05-22; linux version
Use Ctrl-D to exit, Ctrl-E for paste mod

Just a few days after SPIKE anouncement, the future of LEGO robots seems now to be very very linked to linux, python and opensource

Me happy! 🙂

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()

 

 

O meu próprio Media Center – parte 1

This post is part 1 of 2 of  O meu próprio Media Center

Parte 1 – Enquadramento

Começa a formar-se uma tradição familiar: a cada novo filho um novo Media Center.

Da primeira vez a ideia inicial foi aproveitar spares para implementar uma forma da cara metade (em licença maternal) poder ver durante o dia os episódios do House que na altura passavam a horas aberrantes.

Cá em casa spares é coisa que nunca faltam, ainda restava qualquer coisa de uns anos antes quando ao ir morar sozinho optei por por não comprar TV e sim usar o computador principal como misto de posto de trabalho e consola de jogos (uma placa receptora de TV da Hauppauge, um sistema de som surround Cambridge, um monitor CRT de 19″). Só que já na altura estes não eram novos e eu não pretendia gastar dinheiro por isso… como já tinha começado a dar uns passos com o Ubuntu descobri uma versão mais leve deste que integrava o MythTV (um projecto open source que implementa uma solução completa de Media Center): o Mythbuntu.

Depois de algumas afinadelas iniciais (sobretudo no sentido de baixar o ruído de funcionamento e melhorar o aspecto externo para poder passar despercebido na sala de estar, trocando o CRT de 19″ pela TV LCD de 32″ oferta de casamento) teve grande sucesso por permitir fugir aos anúncios: podiamos por exemplo começar a ver um telejornal 20 minutos depois do início da emissão e acabar de vê-lo em real time tendo saltado toda a publicidade e lavagem cerebral. Passados quase 5 anos ainda nos rimos quando vemos os prestadores de televisão terrestre anunciarem como novas as funcionalidades que usamos há tanto tempo.

Sim, temos apenas 4 canais… mas para o que precisamos chega e sobra e não encontramos nos canais terrestres qualidade/oferta suficientes que justifiquem subscrever o serviço, ainda mais quando os únicos operadores que se dignam cablar até à nossa porta estão na minha lista negra (e o ADSL que também temos não permite IPTV por imposição de um desses operadores, tal é a livre concorrência neste país)

A configuração foi sofrendo alguns upgrades e ajustes até que Portugal entrou na era da TDT e eu me apercebi que tinha cerca de 2 anos até ser desligado o sinal analógico de TV. Como a TV entretanto avariara e já não valia a pena reparar (a experiência de TV através do Media Center era tão ou mais satifastória exceptuando o futebol em que víamos os golos 10 segundos depois do vizinho de cima ter gritado) ficámos com 2 opções: comprar uma nova TV que já permitisse TDT ou adicionar uma placa DVB-T ao Media Center. Na altura as televisões inteligentes ainda estavam a despontar, com preços ainda muito elevados e com normas ainda muito mal definidas por isso foi a placa.

Como o processador e a placa gráfica não tinham capacidade para exibir a alta definição da TDT [que mais tarde vim a descobrir ser apenas marketing já que as emissões TDT em Portugal são de 720p e não de 1080p, talvez o operador de TDT vender também serviços terrestres e o Estado não estar para se chatear tenha algo a ver com isso] foi necessário nesta segunda encarnação do Media Center passar a ter dois sistemas: um Frontend na sala, pequeno e praticamente silencioso, somente para visualização ou audição e um Backend mais volumoso, num quarto vazio, para recepção do sinal TDT e armazenamento dos ficheiros.

Passada a reacção «há cabos pela casa toda!!!» repetiu-se o sucesso da primeira versão, agora com o pequenote a dominar também o conceito de «pausar» os desenhos animados.

Entretanto as televisões inteligentes baixaram de preço e aproveitei a licença parental para substituir a TV avariada – sai um LCD de 32″ [e 20 kg!] por um LED de 40″ bem mais leve e ocupando o mesmo espaço.

Só que a placa gráfica do Frontend [e tudo o resto cá em casa excepto os laptops] não permite ligação digital e a motherboard não permitia adicionar nenhuma placa gráfica moderna por isso foi necessário actualizar o Frontend. E uma vez que o novo sistema vem com bastante mais recursos, voltamos a ter ambas as funcionalides (Frontend e Backend) num só sistema.

Nos próximos artigos explico como configurar um Media Center que permite tirar partido das funcionalidades de uma SmartTV.