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 Remote Control

Uma incursão rápida em Python e Tkinter (uma library para aplicações GUI muito fácil de usar no modo Google-Copy-Paste) para poder comunicar com o SBrick a partir do meu laptop Ubuntu sem usar a linha de comando:

SBrick-RemoteControl

#!/usr/bin/env python 

from Tkinter import *
from time import sleep
from subprocess import call

def quit():
  global Window
  Window.destroy()

  return;

def STOP():

  call("gatttool -b 00:07:80:7F:28:E1 -i hci0 --char-write --handle=0x0025 --value=0002",shell=True)
  call("gatttool -b 00:07:80:7F:28:E1 -i hci0 --char-write --handle=0x0025 --value=0003",shell=True)
  return

def NORTH():

  time=scale.get()
  call("gatttool -b 00:07:80:7F:28:E1 -i hci0 --char-write --handle=0x0025 --value=010301FF",shell=True)
  call("gatttool -b 00:07:80:7F:28:E1 -i hci0 --char-write --handle=0x0025 --value=010200FF",shell=True)
  sleep(time)
  STOP()
  return

def SOUTH():

  time=scale.get()
  call("gatttool -b 00:07:80:7F:28:E1 -i hci0 --char-write --handle=0x0025 --value=010300FF",shell=True)
  call("gatttool -b 00:07:80:7F:28:E1 -i hci0 --char-write --handle=0x0025 --value=010201FF",shell=True)
  print("Backward")
  sleep(time)
  STOP()
  return

def EAST():

  time=scale.get()
  call("gatttool -b 00:07:80:7F:28:E1 -i hci0 --char-write --handle=0x0025 --value=010301FF",shell=True)
  call("gatttool -b 00:07:80:7F:28:E1 -i hci0 --char-write --handle=0x0025 --value=010201FF",shell=True)
  print("RIGHT")
  sleep(time)
  STOP()
  return

def WEST():

  time=scale.get()
  call("gatttool -b 00:07:80:7F:28:E1 -i hci0 --char-write --handle=0x0025 --value=010200FF",shell=True)
  call("gatttool -b 00:07:80:7F:28:E1 -i hci0 --char-write --handle=0x0025 --value=010300FF",shell=True)
  sleep(time)
  STOP()
  return


Window = Tk()
Window.title("SBrick Remote Control v0.1")

B_North = Button(text = "N", command = NORTH)
B_North.grid(row=0, column=1)

B_West = Button(text = "W", command = WEST)
B_West.grid(row=1, column=0)

B_STOP = Button(text = "STOP", command = STOP)
B_STOP.grid(row=1, column=1)

B_East = Button(text = "E", command = EAST)
B_East.grid(row=1, column=2)

B_South = Button(text = "S", command = SOUTH)
B_South.grid(row=2, column=1)

scale = Scale(Window, from_=0.125, to=2.5, digits=3, resolution=0.125, orient=HORIZONTAL, length=250, label="Time")
scale.grid(row=3,column=0,columnspan=3)

B_QUIT = Button(text = "QUIT", command = quit)
B_QUIT.grid(row=4, column=1, )

mainloop()

 

 

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?

Raspberry Pi como alternativa LEGO WeDo

Para a primeira aplicação prática montei o Roaring Lion do conjunto LEGO WeDo (as instruções de montagem deste e outros modelos simples estão disponiveis no site LEGO Education).

raspberrypi-lego-roaring-lionO leão é muito simples: fazendo rodar o motor num sentido ele levanta-se nas patas dianteiras, no outro sentido volta a deitar-se (como não há sensor de posição é necessário afinar os tempos à mão)

lego-roaring-lionNuma primeira fase testei comandar o leão remotamente a partir de uma sessão SSH, criando para isso um script (lion.py) em python:

#!/usr/bin/python3

import sys
from time import sleep
import pifacedigitalio

DELAY_UP = 0.17       # o leão custa um pouco mais a subir
DELAY_DOWN = 0.14     # do que a descer

pifacedigital = pifacedigitalio.PiFaceDigital()
led7=pifacedigital.leds[7]
led6=pifacedigital.leds[6]
led7.turn_off()
led6.turn_off()

comando='.'

while(comando!="s"):
 comando=input("Comando: ")
 if (comando=="c"):
   print("Para cima!")
   led7.turn_on()
   sleep(DELAY_UP)
   led7.turn_off()
 elif (comando=="b"):
   print("Para baixo!")
   led6.turn_on()
   sleep(DELAY_DOWN)
   led6.turn_off()
 elif (comando=="s"):
   print("Tchau!")
 else:
   print("?") # comando desconhecido

É necessário dar permissões de execução ao script:

sudo chmod u+x lion.py

Para correr, basta invocar directamente o script:

./lion.py

Os comandos são simples:

  • c – para cima
  • b – para baixo
  • s – para sair

Funciona bem mas seria bem melhor se o leão estivesse sempre à espera de um comando e pudessemos usar o rato em vez do teclado, por exemplo a partir de um browser.

Para isso precisamos de um servidor (daemon) http – poderia ter ido para o apache mas preferi ir à procura de um servidor http mais leve e encontrei o Monkey (no passado tive boas experiências com o lighttpd e existe também o nginx):

Começamos por adicionar o repositório para usar a versão directamente mais recente em vez da que vem do mundo Debian:

sudo nano /etc/apt/sources.list

e adicionar a linha

deb http://packages.monkey-project.com/primates_pi primates_pi main

depois actualizar a cache:

sudo apt-get update

e instalar todos os componentes:

sudo apt-get install monkey          
                  monkey-liana       
                  monkey-logger      
                  monkey-dirlisting  
                  monkey-cgi         
                  monkey-fastcgi     
                  monkey-mandril     
                  monkey-cheetah     
                  monkey-auth

(embora só seja, mesmo necessários o monkey e os módulos liana e cgi )

Para testar basta aceder por browser na porta 2001:

http://192.168.1.123:2001/

(o conteúdo desta página está ficamente em /usr/share/monkey/)

Para poder invocar scripts de python a partir de páginas html é necessário recorrer a CGI (penso que seria possível fastcgi mas esta não é de todo a minha especialidade).

Criei uma pasta só para armazenar os scripts que vão ser acedidos por browser:

sudo mkdir /usr/share/monkey/cgi-bin

É necessário configurar o monkey para carregar o módulo CGI:

sudo nano /etc/monkey/plugins.load

basta descomentar a linha (mentendo os espaços em branco):

Load /usr/lib/monkey/monkey-cgi.so

além disso é necessário mudar a conta com que o monkey corre para poder utilizar as libraries do PiFace:

sudo nano /etc/monkey/monkey.conf

(basta mudar de www-data para root)
E finalmente reiniciar o monkey:

sudo service monkey restart

Para testar, colocar na pasta /usr/share/monkey/cgi-bin o ficheiro hello.cgi:

#!/usr/bin/python3
print("Content-Type: text/html")     # HTML is following
print()                              # blank line, end of headers
print("<TITLE>CGI script output</TITLE>")
print("<H1>This is my first CGI script</H1>")
print("Hello, world!")

(eventualmente dar-lhe permissões de execução – sudo chmod u+x)

E aceder por browser: http://192.168.1.123:2001/cgi-bin/hello.cgi

Confirmado o funcionamento, criar 2 scripts para controlo do leão:

E uma página que se limita a disponibilizar dois links para os dois scripts:

<html><head>
<title>Pitagoras</title>
</head><body>
<p><a href="cgi-bin/cima.cgi">Para cima!</a></p>
<p><a href="cgi-bin/baixo.cgi">Para baixo!</a></p>
</body></html>

E…

funciona!

É pena que o CGI obrigue a que cada vez que é dado um comando seja reinicializado o python e recarregados os módulos – cada comando demora por isso demora 2 ou 3 segundos. Os melhoramentos ficam para outra altura.