Arduino Starter Kit

1 Un post sobre Arduino

Des de fa alguns anys que dins del món de programari lliure, o del moviment maker, o des d’entorns educatius i formatius de tecnologia, se sent parlar i es desenvolupa una activitat creixent al voltant d’Arduino (i de Raspberry Pi, Scratch, Processing…)

Em venia de gust provar l’Arduino i això em va dur a comprar, fa uns mesos, un Arduino Starter Kit per a fer-ne els experiments i captar-ne les “sensacions”. Finalment, he trobat temps per completar els experiments i és el moment d’explicar què m’he trobat.

L’Starter Kit deu ser, segurament, la forma més suau d’introduir-se a l’Arduino. És una caixa que inclou una placa Arduino One, una placa protoboard, components electrònics diversos, cables i ponts de connexió i un manual amb quinze experiments -cap d’ells complex, però algun de força vistós- per a realitzar amb el material inclòs.

Clarament l’Arduino Starter Kit està orientat a un públic jove. Les nenes i nens de ESO no haurien de tenir cap dificultat en la realització dels experiments i comprendre la lògica de funcionament dels circuits i els diferents components utilitzats. En cap cas es tracta d’experiments perillosos. Potser els circuits que involucren el motor de continua són els més “complexos” i els que poden presentar major dificultat de muntatge, però els resultats son espectaculars.

Però no us penseu que Arduino és tracta d’una joguina. Les noies i els nois més grans, a nivells de Batxillerat, però també universitaris, poden trobar a l’Arduino una plataforma excel·lent per a la realització de projectes. Fora dels entorns formatius, Arduino ha esdevingut una de les plaques preferides pels aficionats a l’electrònica i pel moviment Maker. Arduino es presenta en diferents versions i existeix una versió industrial per a l’us professional.

2 Però què és Arduino?

(traduccio lliure de https://www.arduino.cc/en/Guide/Introduction)

“Arduino és una plataforma de prototipatge de codi obert basada en hardware i software senzill i fàcil d’usar. Les plaques Arduino són capaces de llegir entrades: com la llum a un sensor, un dit prement un botó, o una piulada de Twitter, i transformar-les en sortides: activant un motor, encenent un LED, o publicant alguna cosa a Internet. Es pot indicar a la placa què ha de fer tot enviant-li instruccions al microcontrolador que porta muntat. Per a aconseguir-ho es fa us del llenguatge de programació de l’Arduino (basat en el llenguatge Wiring), i en l’entorn de desenvolupament integrat d’Arduino (Integrated Development Enviroment, o IDE), basat en l’entorn Processing.

Amb el pas dels anys Arduino ha estat el cervell de milers de projectes, desde objectes quotidians fins a instruments científics complexos. Una comunitat mundial de ‘makers’ -estudiants, aficionats, artistes, programadors i professionals- s’ha aplegat al voltant d’aquesta plataforma de codi obert. Les seves contribucions hab afegit una quantitat increible de coneixement accessible que pot ser de gran ajuda tant per als principiants com per als més experts.

Arduino va neixer al Ivrea Interaction Design Institute com una eina d’utilització fàcil per al prototipatge ràpid adreçada a estudiants sense fonaments en electrònica i programació. Tan aviat com va arribar a una comunitat més gran, la placa Arduino va començar a canviar per adaptar-se a noves necessitats i desafiaments, diversificant la seva oferta desde plaques simples de 8 bits, fins productes per a aplicacions de Internet of Things (IoT), wearables, impressió 3D i sistemes encastats. Totes les plaques Arduino són completament de codi obert, permetent als usuaris construir-les pels seus propis mitjans i adaptar-les als seus requeriments particulars. El programari també és de codi obert i evoluciona amb les constribucions dels usuaris d’arreu del món.

Mercès a la seva accessibilitat i simplicitat d’us, Arduino s’utilitza en milers de projectes i aplicacions diferents. El programari d’Arduino és d’us senzill per als principiants però, a l’hora, prou flexible per usuaris experimentats. Es pot executar en Mac, Windows i Linux. Els professors i els estudiants el fan servir per construir instruments científics barats, o per provar principis de física i química, o per a iniciar-se en la programació i la robòtica. Els dissenyadors i els arquitectes construeixen prototipus interactius. Músics i artistes el fan servir en instal·lacions i per experimentar amb instruments musicals nous. Els ‘Makers’, per descomptat, el fan servir per construir molts dels projectes que s’exhibeixen a les ‘Maker Faire’, per exemple. Arduino és una eina clau per a l’aprenentatge de novetats. Tothom – nens, aficionats, artistes, programadors – es pot posar mans a l’obra ja sigui seguint les instruccions pas a pas d’un kit, o compartint idees amb altres membres de la comunitat Arduino.

Hi han molts altres microcontroladors i plataformes de microcontrolador per a la construcció de sistemes físics interactius (“physical computing”). Parallax Basic Stamp, BX-24 de Netmedia, Phidgets, Handyboard del MIT, i molts altres que ofereixen funcionalitats similars. Totes aquestes eines encapsulen els detalls més farragosos de la programació de microcontroladors i el empaqueten en una interficie d’usuari més senzilla d’utilitzar. Arduino també simplifica el procés de treball amb microcontroladors i, a més, ofereix alguns avantatges per als professors, estudiants, i aficionats que no tenen els altres sistemes:

  • Barat – Les plaques Arduino són relativament barates si es comparen amb altres plataformes de microcontroladors. La versió més barata d’Arduino es pot muntar a mà, i els mòduls de version ja muntades, no arriben als 50 dòlars.
  • Multiplataforma – L’entorn de programació d’Arduino s’executa a Windows, Macintosh OSX, i distribucions Linux. La majoria de plataformes de microcontroladors estan limitades a Windows.
  • Entorn de programació senzill – L’entorn de programació és fàcil d’usar pels principiants, però prou flexible com per a que els usuaris avançats el puguin fer servir eficaçment. Per als mestres, l’entorn integrat està basat en l’entorn de programació ‘Processing’, per tant, els estudiants que estiguin aprenent a programar en aquest es trobaran que els serà familiar la forma de treballar de l’IDE d’Arduino (veure nota).
  • Codi obert i programari extensible – El programari Arduino es codi obert i es pot extendre. El llenguatge de programació es pot ampliar mitjançant llibreries C++, i la gent aque vulgui comprendre els detalls més tècnics poden fer els salt des del llenguatge de l’Arduino IDE al llenguatge de programació C per AVR en que està basat. També es pot afegir codi AVR-C directament als programes d’Arduino si es vol.
  • Codi obert i maquinari extensible – Els esquemes de les plaques Arduino es publiquen amb llicència Creative Commons, per tant els dissenyadors mes experimentats poden fer les seves pròies versions dels mòduls, extendre-les o millorar-les. Fins i tot, un principiant pot construir la seva pròpia versió dels mòduls en una placa protoboard, per a comprendre’n el funcionament i per estalviar diners.”

Nota: realment l’IDE de l’Arduino està basat en el IDE Wiring (basat en el llenguatge C), que al seu temps ho està en el IDE de Processing (basat en llenguatge Java)

3 Especificacions d’Arduino One

Les podeu trobar a la pàgina oficial. Les reprodueixo a continuació:

Technical specs
Microcontroller ATmega328P
Operating Voltage 5V
Input Voltage (recommended) 7-12V
Input Voltage (limit) 6-20V
Digital I/O Pins 14 (of which 6 provide PWM output)
PWM Digital I/O Pins 6
Analog Input Pins1 6
DC Current per I/O Pin 20 mA
DC Current for 3.3V Pin 50 mA
Flash Memory 32 KB (ATmega328P) of which 0.5 KB used by bootloader
SRAM 2 KB (ATmega328P)
EEPROM 1 KB (ATmega328P)
Clock Speed 16 MHz
Length 68.6 mm
Width 53.4 mm
Weight 25 g

I també l’esquemàtic.

4 Els circuits.

L’Starter Kit proposa la construcció de quinze circuits molt senzills. Per a cada circuit es proporciona:

  • la llista de components a utilitzar
  • l’esquema
  • la foto del circuit muntat
  • la descripció pas a pas del muntatge dels components
  • el codi C comentat que cal compilar i carregar a l’Arduino
  • com provar el circuit
  • propostes d’ampliació i variacions

4.1 Experiment 1.

S’expliquen els contactes de la protoboard; S’enuncien els rudiments dels circuits elèctrics i la llei d’Ohm, i es fan circuits mínims en serie i paral·lel. L’Arduino només es fa servir per proporcionar l’alimentació dels circuits.

4.2 Experiment 2.

És un experiment mínim que permet introduir l’Arduino IDE. Es programa una seqüència d’encesa i apagat de tres leds fent servir sortides digitals. S’aprèn a compilar i carregar els programes a la placa. Ens planteja l’estructura bàsica dels programes Arduino, amb la funció setup() que s’executa al començament de l’execució i que s’aprofita per a inicialitzar l’estat de la placa; i la funció loop() que és una funció que s’executa contínuament, i que és on es programa la funcionalitat desitjada. Les funcions setup() i loop() apareixen sempre en tots els programes d’Arduino.

S’introdueix una tècnica per reconèixer els canvis d’estat als ports d’entrada de l’Arduino.

4.3 Experiment 3.

S’introdueix el sensor de temperatura TMP36 que es capaç de proporcionar un rang de tensions de sortida que varia proporcionalment a la temperatura en graus centígrads. Es mostra l’ús de les entrades analògiques i una tècnica simple d’escalat de valors. S’introdueix el Monitor Sèrie, que permet obtenir informació i missatges provinents de la placa Arduino, i també per enviar senyals a la placa. Es fan servir les sortides digitals per simular un termòmetre fet amb leds.

4.3.1 Comunicació entre Arduino i Host pel port sèrie (USB) amb altres llenguatges

La comunicació sèrie entre l’ordinador (el host) i la placa Arduino mitjançant el port sèrie (USB) es pot fer amb varietat de llenguatges, no només amb l’Arduino IDE.

En particular, és molt senzill fer la comunicació amb aplicacions desenvolupades amb C/C++ perquè es fa servir la llibreria C estàndard. És dir, amb C/C++ n’hi ha prou amb obrir el port /dev/ttyACM0 amb fopen per a lectura/escriptura i fer servir la resta de funcions de I/O de la la llibreria estàndard: fread, fwrite, fclose…

Alternativament, també es poden fer servir les crides de baix nivell: open, write, read, close… la tria d’un o altre grup de funcions, doncs, dependrà d’altres criteris, com pot ser el grau d’abstracció que es requereixi en la comunicació entre host i Arduino. En general, però, sempre caldrà tenir en compte mantenir la consistència amb el mètode triat (Llibreria Estàndard de C, o Crides al Sistema),

Amb Python, la comunicació entre Arduino i Host es pot fer fàcilment amb la llibreria pySerial.

La llibreria s’ha de carregar. En el meu cas que faig servir una distribució Lubuntu 15.04 he de fer:

sudo apt-get install python-serial

El següent és un exemple bàsic que permet llegir els missatges que venen de la placa Arduino:

#!/usr/bin/env python
# -*- coding: utf-8 -*-

import serial
 
ser = serial.Serial('/dev/ttyACM0', 9600)
while True:
   print ser.readline()

Amb Java, es pot fer servir la llibreria RxTx. Es tracta d’un jar amb classes java i d’una so (per Linux, Mac) o dll (per Windows) que cal instal·lar. Val a dir que aquesta llibreria va inclosa amb l’entorn Processing, així que si teniu instal·lat aquest entorn ja teniu la RxTx al vostre sistema.

4.4 Experiment 4.

En aquest experiment es fan servir tres fotoresistències per a modular els senyals dels components blau, ved i vermell d’un LED RGB de càtode comú: en funció del grau d’il·luminació de cadascuna de les fotoresistències s’aconsegueix que la llum del LED variï entre el blanc (tots els sensors totalment il·luminats), tons i barreges de colors (diferents intensitats de llum sobre cada sensor), o l’apagat (amb els sensors de llum tapats).

Per a simular les sortides de diferents intensitats amb les sortides digitals s’aprofita que les sortides 3,5,6,10 i 11 de l’Arduino admeten la modulació PWM, (és dir, regular el cicle de treball del pols digital i, per tant, modular-ne el valor del component de CC). En la programació del codi es té en compte el temps necessari per a la conversió AD a les entrades connectades als sensors.

4.5 Experiment 5.

El servomotor és l’estrella de l’experiment 5. L’alimentació de 5V de l’arduino és suficient per alimentar el servomotor. Al tractar-se d’una càrrega inductiva cal protegir la placa dels corrents espúris produits pel servo: en aquest circuit s’afegeixen un parell de condensadors de desacoblament,

Al codi cal destacar

  • L’utilització de la llibreria Servo, que encapsula la utilització de sevomotors de Parallax.
  • L’us de la funció map() (veure nota) que permet fer un mapeig escalat lineal entre un rang d’entrada i un de sortida.

Per a saber més de motors i servos, se’ns recomana el llibre Making Things Move de Dustyn Roberts i l’adreça http://robives.com/mechs

nota: aquesta funcio map() no té res a veure amb la funció homònima que es pot trobar als llenguatges que segueixen el paradigma de programació funcional!

4.6 Experiment 6.

Del moviment al sò. Conceptualment és similar a l’experiment 4. Es fa servir una fotoresistència per modular el senyal que ataca un brunzidor piezoelèctric. El senyal del sensor es converteix AD a un valor que es fa correspondre amb map() (veure nota) a un valor dins el rang de control de la sortida PWM que ataca al brunzidor.

La novetat en aquest circuit és que en comptes de fer servir la funció analogWrite() es fa servir la més adequada tone(): Amb analogWrite() la freqüència que s’envia per la sortida és fixa però amb un cicle de treball (relació entre temps en estat alt i període del senyal) variable; amb tone() el cicle de treball és sempre del 50%, però canvia la freqüència del senyal.

Al codi també s’introdueix la funció millis() per controlar un temporitzador.

4.7 experiment 7.

Un altre experiment amb el brunzidor. En aquesta ocasió se substitueix el sensor de llum per una escala de resistències combinada amb uns interruptors, de forma que a la pulsació de cada interruptor li correspon un valor de tensió en l’entrada AD de l’Arduino. A cada valor d’entrada se li fa correspondre una freqüència, amb la que s’ataca al brunzidor.

la novetat al codi és la utilització d’un array de C per mantenir la taula de freqüències, i la utilització de noTone() per silenciar el brunzidor quan no hi ha cap interruptor premut.

Una taula amb les freqüències de les notes musicals es pot consultar a http://arduino.org/frequencies.

4.8 Experiment 8.

En aquest experiment es fa servir un interruptor d’inclinació per a simular un rellotge de sorra: al moure l’interruptor que està connectat a una entrada digital que s llegeix amb digitalRead(), es restaura l’estat de les sortides digitals que estan connectades a uns leds. Aleshores, a la funció loop(), en cada iteració s’observa l’hora actual que s’obté amb millis() i es compara amb l’hora en que s’ha encès el darrer LED. Si l’interval de temps supera un valor prefixat, s’encén el següent LED i s’actualitza la marca.

4.9 Experiment 9.

Es presenta el motor de continua. El motor de CC és de velocitat i sentit de gir variables. la velocitat de gir es pot controlar variant la tensió aplicada a les seves connexions (dins d’uns límits), i el sentit de gir es pot canviar invertint-ne la polaritat.

Tanmateix, fins i tot amb el petit motor inclòs a l’Starter Kit, l’Arduino no és capaç de proporcionar prou corrent per a alimentar motors directament: Arduino One no pot proporcionar més de 40mA per les sortides digitals.

El que es fa al circuit és connectar el motor a una font externa i controlar-ne l’alimentació fent servir un transistor MOSFET IRF520 com a interruptor electrònic, connectant-ne la porta a una de les sortides PWM i fent el control efectiu de l’encesa i apagada amb un interruptor connectat a una de les entrades digitals.

Al manual del kit proposen fer servir una pila de 9V per a la font externa. També es pot fer servir una font d’alimentació de les que es poden trobar a qualsevol basar xinès.

Addicionalment, es fa servir un díode rectificador N4007 connectat en oposició a l’alimentació del motor, per a proporcionar un camí de descàrrega per als corrents auto-induits quan aquest es desconnecta de l’alimentació i es va frenant fins aturar-se.

4.10 Experiment 10.

A l’experiment anterior es presentava el motor de CC i es muntava un circuit molt senzill per a engegar-lo i aturar-lo, però s’indicava que és possible regular-ne la velocitat i l sentit de gir. En aquest experiment es fa servir un circuit integrat L293D de driver per a motors per aconseguir aquest control.

L’ús de l’L293D simplifica el control dels motors: un xip substitueix el circuit del MOSFET i al díode de protecció. Però, a més, és possible realitzar el control de velocitat i la inversió de gir (un pont H) amb aquest CI.

És una bona idea revisar l’especificació de l’L293D per revisar diferents modalitats de connexió, o la connexió de fins a quatre motors amb aquest driver.

4.11 Experiment 11.

Es presenta el dispositiu LCD de visualització LCM1602C, es tracta d’un pantalla LCD de 16×2 caràcters. El muntatge connecta les sortides digitals de l’Arduino al display LCD per a mostrar missatges que canvien aleatòriament quan es mou l’interruptor d’inclinació connectat a una de les entrades digitals. És un projecte similar al de l’experiment 8, només que en comptes d’enviar els missatges aleatoris pel monitor sèrie, els mostra a l’LCD.

La lògica de comunicació entre Arduino i l’LCD s’encapsula a la llibreria LiquidCrystal. La llibreria s’explica amb detall a la url: http://arduino.org/lcd.

Pel que fa al muntatge, val a dir que hi ha alguna etiqueta de l’esquema al manual que no coincideix exactament amb l’etiqueta impresa al component LCD i potser pot produir alguna confusió. En tot cas, una revisió de les especificacions de l’LCM1602C haurien de ser suficients per esvair qualsevol dubte en el cablatge.

4.12 Experiment 12.

Un altre experiment amb el servomotor. En aquesta ocasió es fa servir el brunzidor piezoelèctric com a sensor, en lloc d’actuador. El brunzidor, a més de produir so, també el pot captar i, per tant, en aquesta ocasió es connecta a una entrada analògica de l’Arduino, enlloc de a una sortida.

El circuit de l’experiment fa un comptador de tocs (per exemple, el soroll de picar de mans).

El programa és un exemple de com implementar una senzilla màquina d’estats. El funcionament implementat va fent transicions des de l’estat de repòs (leds taronja i verd apagats, led vermell encès, servo en posició de “tancat”), a l’estat d’un toc vàlid captat (leds verd i vermell apagats, led taronja encès, servo “tancat”), dos tocs vàlids captats (mateixes condicions), i tres tocs vàlids captats (leds vermell i taronja apagats, led verd obert, i servo en posició d'”obert”), on roman fins que es rep un reset que torna a l’estat de repòs original.

4.13 Experiment 13.

l’Starter Kit ens proposa la construcció d’un “component” capacitiu amb paper d’alumini que es pugui fer servir amb la llibreria CapacitiveSensor de Paul Badger.

El component capacitiu no és més que una làmina de paper d’alumini.

El principi de funcionament d’aquest “sensor” és la càrrega i descàrrega de les capacitats paràsites que apareixen al circuit. Per a major detall, podeu consultar http://playground.arduino.cc/Main/CapacitiveSensor

També és interessant que, a diferència d’altres llibreries usades en els experiments, cal descarregar la CapacitiveSensor d’Internet i instal·lar-la correctament a l’Arduino IDE per a poder fer-la servir.

4.14 Experiment 14.

És possible “parlar” amb l’Arduino amb varietat de llenguatges, entorns i aplicacions, com podeu comprovar a http://playground.arduino.cc/Main/Interfacing.

A l’experiment 14 s’envia un senyal variable des de l’arduino pel monitor sèrie (pel port USB). El senyal és genera amb un divisor de tensió fet amb un potenciòmetre connectat a una entrada analògica, però en aquesta ocasió no es manipula cap sortida si no que el valor capturat s’envia pel port sèrie.

La novetat de l’experiment és que en comptes de capturar la dada enviada per la placa amb el Monitor Sèrie de l’Arduino IDE es fa servir una petita aplicació desenvolupada amb Processing. De la wiki: “Processing és una aplicació de codi obert amb un llenguatge per a la programació d’imatges, animació, i so. El processing és un projecte de codi lliure iniciat per Ben Fry (Institut Ample) i Casey Reas (UCLA Design / Media Arts), i és desenvolupat per artistes i dissenyadors com a alternativa a eines de programari patentades en el mateix camp, com Macromedia Flash o Director.”

Processing i Arduino IDE són molt similars. El paral·lelisme va més enllà del look-and-feel de l’entorn: els programes amb l’Arduino IDE són, bàsicament, un setup() i un loop(); amb Processing tenim també un setup() que s’executa un cop a l’inici de l’execució; i un draw(), que al igual que loop() s’executa contínuament.

Processing compta amb moltes llibreries realitzades i depurades per usuaris i compartides amb la comunitat, igual que l’Arduino IDE. En particular, compta amb una llibreria procesing.serial basada en la RxTx per a realitzar comunicacions pel port USB (serie). Internament, RxTx es basa en una llibreria nadiua al sistema operatiu hoste, que s’encapsula amb el mecanisme JNI de Java, i unes classes java que exposen la interfície de programació.

El programa proposat amb Processing escolta el port serie i, segons les dades que va rebent (entre 0 i 255, que corresponen als nivells de tensió variables amb el potenciòmetres connectat a l’entrada analògica de l’Arduino) es va canviant el color de fons d’una pantalla que mostra una imatge del logo d’Arduino.

4.14.1 Experiment 14 Amb Python, Pygame i pySerial

El cas és que m’he trobat un parell de problemes al codi del manual: el primer és que la imatge que es fa servir és referencia a través d’una URL. Aquesta URL ja no està disponible i cal canviar-la per una altre que que sí es pugui accedir. I segon: em fa l’efecte que el codi que es fa servir no està actualitzat a la darrera versió de la llibreria.

Vistes aquestes “dificultats” he optat per buscar una solució alternativa. Processing està molt bé, però com Python m’agrada molt, he assajat una solució amb Python i les llibreries Pygame i pySerial. Vet aquí el codi:

#!/usr/bin/env python
# -*- coding: utf-8 -*-

import pygame, sys, serial
from pygame.locals import *

if __name__ == "__main__":
    pygame.init()
    FPS = 25 # frames per second setting
    fpsClock = pygame.time.Clock()
    
    # set up the window
    board = pygame.display.set_mode((400, 300), 0, 32)
    pygame.display.set_caption('Arduino')

    # set up serial port
    ser = serial.Serial("/dev/ttyACM0", baudrate=9600)
    
    while True: # the main loop
        if ser.isOpen():
            A0_385 = ser.read(385) # 385 = 9600 / FPS
            A0 = ord(A0_385[0])
            FILL_COLOR = (A0, A0, A0)
        
            board.fill(FILL_COLOR)
        
        for event in pygame.event.get():
            if event.type == QUIT:
                ser.close()
                pygame.quit()
                sys.exit()

        pygame.display.update()
        fpsClock.tick(FPS)

Els programes fets amb Pygame tenen aquesta mateixa estructura setup-loop que es pot trobar als programes de Processing, o als programes de l’Arduino. És fàcil, doncs, “anar” de l’un a l’altre.

Val a dir que m’he trobat amb una dificultat que no havia previst: les diferents velocitats entre la producció de dades de la placa i la velocitat de consum, donada pel nombre d’iteracions per segon del bucle de lectura de dades i d’actualització de pantalla, que està controlat per fpsClock.tick(FPS).

La solució ha estat “descartar frames” que és el que aconsegueixo amb:

A0_385 = ser.read(385) # 385 = 9600 / FPS
A0 = ord(A0_385[0])
FILL_COLOR = (A0, A0, A0)

És dir llegeix 385 caràcters el buffer (385 = bauds / fps), però només té en compte el primer. Més que una dada precisa que permeti buidar el buffer de lectura en cada iteració, 385 (o 400) és, més aviat, un ordre de magnitud que es podria ajustar.

En tot cas, aquest valor funciona bastant bé, i movent el potenciòmetre de la placa, aconsegueixo que el color de fons de la pantalla variï gradualment en una escala de grisos entre el blanc i el negre.

4.14.2 Una “variació” de l’experiment 14 amb Scratch for Arduino

Scratch for Arduino (S4A) és un desenvolupament realitzat al Citilab de Cornellà.

De la pàgina web http://s4a.cat/index_ca.html: “Què és S4A? S4A és una modificació d’Scratch que permet programar la plataforma de hardware lliure Arduino d’una manera senzilla. Proporciona blocs nous per tractar amb sensors i actuadors connectats a una placa Arduino. També compta amb un panell de sensors similar al de la PicoBoard.

La finalitat principal del projecte és atreure gent al món de la programació. Un altre objectiu és proporcionar una interfície d’alt nivell per a programadors d’Arduino amb funcionalitats tals com la interacció amb un conjunt de plaques mitjançant esdeveniments d’usuari.”

Som-hi. Realment, aquest versió de l’experiment no segueix ben bé la mateixa lògica perquè amb S4A no puc llegir directament el monitor sèrie. El que faig es llegir el senyal de l’entrada analògica amb un sensor d’Scratch, i faig servir aquest valor per moure un sprite amb el gat d’Scratch per la pantalla.

El procés és el següent.

Descarrego i instal·lo l’S4A i el firmware de connexió (el programa d’interfície amb Scratch que realment s’executa a la placa Arduino i que és el que controla el port serie).

El circuit a la placa Arduino és el de l’experiment 14 tal com apareix al manual: un potenciòmetre fent de divisor de tensió entre Vcc i GND amb la pota de control connectada a l’entrada analògica A0.

Connecto per USB l’ordinador i l’Arduino i fent servir l’Arduino IDE carrego el firmware a la placa.

Ara engego l’S4A. No ha d’haver cap problema. L’S4A reconeix una placa Arduino connectada.

Aleshores, amb l’Scratch preparo una nova variable v_posx:

1-variable

aquest bloc per a “l’escenari”:

2-escenari

aquest per al gat:

3-gat

Li dono a la bandereta verda i, efectivament, ara tinc un gat corrent per la pantalla controlat pel potenciòmetre del circuit a la placa Arduino. Fixeu-vos en el valor de la variable de l’entrada A0 a les imatges.

4.15 Experiment 15.

I per acabar la revisió de l’Starter Kit, a l’experiment 15 se’ns anima a buscar aquelles andròmines velles que siguin susceptibles d’algun tipus de control electrònic i tractar d’imaginar com es podrien connectar a l’Arduino, i s’aprofita per presentar l’opto-acoblador 4N35.

Títols de crèdit com els d’Star Wars (efecte “rolling”) amb Python

1 Fa molt temps (però no en una galàxia molt llunyana)…

… vaig a anar al cine -i era un dels primers cops que hi anava- per veure “La guerra de las galaxias”, que era com es va traduir el nom de “Star Wars: A new hope”.
La pel·lícula em va impressionar des del primer segon, començant pels imponents títols de crèdit que introduïen la història i que s’allunyaven majestuosament per l’espai.
Per a la realització de l’efecte dels títols allunyant-se es van utilitzar tècniques “artesanals”. En aquest enllaç expliquen com, però una foto val més que mil paraules:
Star-Wars-Intro-Creation-Secret-2
Des de l’arribada de la informàtica personal s’han inventat de forma recurrent sistemes per reproduir l’efecte amb ordinadors. Una cerca a Google ens mostra un munt de resultats.
L’efecte es pot aconseguir directament amb diverses aplicacions, però segueix sent un repte interessant la seva realització per programa. En el post d’avui, doncs, presento un “prova de concepte” feta amb Python per a generar aquest efecte de rolling a uns títols de crèdit.

2 Com es fa una animació?

Es sabut que una animació no és més que la successió ràpida d’imatges (frames, o fotogrames). Per tant, per a fer una animació només cal mostrar una imatge durant un breu instant, a continuació una altre amb un petit canvi, després un altre… la integració de la successió de les imatges estàtiques al nostre cervell és el que provoca la sensació de moviment. Aleshores, per a fer una animació el que he de fer és crear els diferents fotogrames que la formen. La sensació de moviment suau depèn de quantes imatges (o frames) per segon es fan servir: amb 12 frames per segon (fps) s’arriben a notar alguns salts; a 25fps la sensació de moviment és pràcticament perfecte, i és el valor que es fa servir a les càmeres de vídeo.

2.1 Un experiment

Fem un petit experiment. Agafem aquesta imatge que és de 606×700
swtfa

En comptes de visualitzar-la sencera…

  • en visualitzaré només un quadre de 320×240;
  • aniré desplaçant el quadre un píxel per cop cap avall;
  • a una velocitat de 10 píxels per segon, és dir, a 10fps.

Faig servir la funció blit de la llibreria pygame per mostrar només una part de la imatge.

blit-image.py

#!/usr/bin/env python
# -*- coding: utf-8 -*-

import pygame as pg
from pygame.locals import *

pg.init()                           # init pygame
w = 320                             # width
h = 240                             # height
size=(w,h)                          # size
screen = pg.display.set_mode(size)  # init screen
pg.display.set_caption('Star Wars TFA')  # caption
filename = "./swtfa.jpg"            # filename
img=pg.image.load(filename)         # image 606x700
x = 0
y = 0
FPS = 10                            # frames per second setting
framerate = pg.time.Clock()
repeat = True
   
while repeat:                       # iterate
    # put image
    screen.blit(img, (0,0), (x, y, 320, 240)) 
    pg.display.flip() 
          
    # scroll down image
    y = y + 1
    if (y + 240 > 800):
      y = 0

    #10fps
    framerate.tick(FPS)

    # capture events
    for event in pg.event.get():
      if event.type == QUIT:
          repeat = False

# exit          
pg.quit()    
print "done!"

Si executo el programa anterior sembla que la imatge dins el requadre es vagi movent cap avall. He aconseguit crear la sensació de moviment a partir d’una imatge estàtica. Amb els títols de crèdit he de fer el mateix: construiré una imatge amb el text i aniré obtenint-ne els diferents frames sense més que anar desplaçant-me un píxel cap avall per a cada frame. Aleshores, un cop tingui tots els frames base, aplicaré una transformació sobre cada frame per afegir l’efecte d’allunyament.

3 Esquema general

La idea és partir d’un text d’entrada inicial i obtenir, com a resultat final, un vídeo amb l’scrolling del text.
Especifico més: el vídeo serà de format mp4 de 320×240 px.

El procés es pot dividir en sub-processos més senzills. Em plantejo aquests quatre passos:

  • donar al text un format adequat per a ser processat.
  • crear els frames base del vídeo (imatges o frames de 320×240)
  • processar els frames per afegir l’efecte d’allunyament: el resultat de processar cada frame serà un nou frame, també de 320×240.
  • crear un vídeo amb els frames processats.

Som hi.

4 Donar al text un format adequat

Primer de tot, el text a mostrar:

EPISODI V
L'IMPERI CONTRAATACA
Les forces imperials avancen implacablement 
de victòria en victòria sobre els rebels. 
L'Imperi prepara el cop definitiu: 
ha descobert la principal base rebel 
al planeta gelat de Hoth 
i es llença a l'atac per destruir-la.
Però no tot està perdut per 
als rebels. 
Si aconsegueixen retenir prou temps 
a les tropes d'assalt, la flota rebel 
podrà escapar a la nova base secreta...

Ara he de preparar aquest text per a que em sigui fàcil generar els frames de la imatge i processar-los.

La idea és aquesta: vaig a convertir el text en una imatge de 320px d’ample (ample de imatge que coincideixi amb l’ample de frame) per el llarg suficient per encabir-hi el text, més 240px (un frame) addicionals en blanc al principi i 240px (un frame addicional) en blanc al final.

Amb aquestes restriccions (i després d’algunes proves) resulta que la mida 320×800 em serveix.

I quin format? La restricció és fer-m’ho fàcil i que tot sigui molt evident i clar. El format més adequat que he trobat és el pbm:

https://en.wikipedia.org/wiki/Netpbm_format

Es tracta d’un format monocrom molt senzill. Reviso l’exemple que apareix a la wiki:

P1
# This is an example bitmap of the letter "J"
6 10
0 0 0 0 1 0
0 0 0 0 1 0
0 0 0 0 1 0
0 0 0 0 1 0
0 0 0 0 1 0
0 0 0 0 1 0
1 0 0 0 1 0
0 1 1 1 0 0
0 0 0 0 0 0
0 0 0 0 0 0
  • ‘P1’ indica que és el format pbm, es dir monocrom amb dades en format ascii
  • ‘# This is…’ és un comentari
  • ‘6 10’ diu que és una imatge de 6×10 píxels
  • ‘0’ indica punt en blanc ‘1’ punt en negre
  • A més, els espais en blanc, i salts de línia es descarten

Tenint en compte tot l’anterior, genero amb GIMP un llenç de 320×800 de fons negre; hi afegeixo el text deixant 240px abans de l’inici i 240px després.

El resultat és aquest:

intro

5 crear el fitxer “master”

Si examino intro.pbm veig el següent:

albert@eowyn:~/workspace/python/starwars-credits$ head intro.pbm
P1
# CREATOR: GIMP PNM Filter Version 1.1
320 800
1111111111111111111111111111111111111111111111111111111111111111111111
1111111111111111111111111111111111111111111111111111111111111111111111
1111111111111111111111111111111111111111111111111111111111111111111111
1111111111111111111111111111111111111111111111111111111111111111111111
1111111111111111111111111111111111111111111111111111111111111111111111
1111111111111111111111111111111111111111111111111111111111111111111111
1111111111111111111111111111111111111111111111111111111111111111111111
albert@eowyn:~/workspace/python/starwars-credits$ wc intro.pbm
  3660   3668 259707 intro.pbm
albert@eowyn:~/workspace/python/starwars-credits$

La part de la imatge es divideix en línies de 70 caràcters. L’especificació dels formats pbm, pgm, ppm recomana que les línies de dades no superin els 70 caràcters, però només és una recomanació. Aprofito aquest grau de llibertat perquè em sembla que és més senzill transformar el bloc de 70×3657 (3660 línies menys les tres de capçalera) en una bloc de 320(+ 1 de retorn de carro)x800 per a poder moure’m directament als inicis de cada fila d’imatge i agafar blocs de 240 files per a generar els frames. O sigui, em preparo un “master” per a poder generar més senzillament els frames. Ho faig amb el següent codi:

w = 320                                   # width of a frame and base image
h_frame = 240                             # height of a frame
h_img_base = 800                          # height of base image
c = 0                                     # counter
frame_size=(w,h_frame)                    # size of frame
filename_read = "intro.pbm"
frames_folder = "./frames/"
filename_master = "master"
bit = ""

# create master
with open(filename_read, "r") as fr:    
    with open(frames_folder + filename_master, "w") as fm:
        # discards three first header lines
        fr.readline()
        fr.readline()
        fr.readline()

        for i in xrange(0, w * h_img_base):
            c = c + 1
            bit = fr.read(1)
            if bit == '\n':
                bit = fr.read(1)
            fm.write(bit)

            if c == w:
                fm.write('\n')
                c = 0

El resultat de l’anterior és un fitxer master de 800 files de 320 caràcters (més un caràcter de retorn de carro a cada línia).

6 creació dels frames

A partir del master és molt senzill generar els frames. Això ho faig amb el següent codi

# create frames
filename_frame = ""
filename_pattern = "frame_%0#5d.pbm"
frame_count = 0

with open(frames_folder + filename_master, "r") as fr:
    for frame_count in xrange(0, h_img_base - h_frame): 
        filename_frame = filename_pattern % frame_count
        print "[Frame %d]" % frame_count

        with open(frames_folder + filename_frame, "w") as fw:
            fw.write("P1\n")
            fw.write("# CREATOR: Albert Baranguer Codina\n")
            fw.write("%d %d\n" % frame_size)

            fr.seek(frame_count * (w + 1))

            for i in xrange(h_frame ):
                fw.write(fr.readline())

Remarcar que amb

fr.seek(frame_count * (w + 1))

Em situo a l’inici de cada frame, i amb

for i in xrange(h_frame ):
    fw.write(fr.readline())

llegeixo el bloc de 240 línies. En aquest moment, després d’executar el bloc anterior, tinc 560 fitxers pbm (560 = 800 – 240) amb noms de frame_00000.pbm a frame_00559.pbm a la carpeta frames. Cada fitxer té un bloc d’imatge de 240 línies de 320 caràcters, més un de retorn de carro, per fila.

7 EL vídeo sense processar

Puc fer servir els frames generats per visualitzar la versió del vídeo sense l’efecte d’allunyament. Faig servir el següent visualitzador:

player1.py

#!/usr/bin/env python
# -*- coding: utf-8 -*-

import pygame as pg
from pygame.locals import *

pg.init()                           # init pygame
w = 320                             # width
h = 240                             # height
size=(w,h)                          # size
screen = pg.display.set_mode(size)  # init screen  
FPS = 18                            # frames per second setting
framerate = pg.time.Clock()
pg.display.set_caption('STAR WARS CAPTION FX')

filename_processed_pattern = "frame_%0#5d.pbm"
processed_frames_folder = "./frames/"  

filename_frame = ""
repeat = True
while repeat: # iterate
    for frame_count in range(0, 560):
        filename_frame = filename_processed_pattern % frame_count
 
        img=pg.image.load(processed_frames_folder + filename_frame)
        screen.blit(img, (0,0)) # put image 
        pg.display.update()
        pg.display.flip() 
        framerate.tick(FPS)

        for event in pg.event.get():
            if event.type == QUIT:
                repeat = False

        if not repeat:
            break

pg.quit()    
print "done!"

8 Filtre d’allunyament

Un cop tinc tots els frames, aplico a cadascun el filtre “d’allunyament”. La idea és mapejar  el frame rectangular a  un trapezi:

(0,0)--------------------------------------(319,0)
  |                                           |
  |                                           |
  |     (c, e)----------------------(d, e)    |
  |       |                           |       |
  |      |                             |      |
  |     |                               |     |
  |    |                                 |    |
  |   |                                   |   |
  |  |                                     |  |
  | |                                       | |
  ||                                         ||
  |                                           |
(239,0)----------------------------------(319,239)

és dir, vull una funció que transformi els punts del rectangle del frame en punts del trapezi definit per (c,e) (d,e) i els cantons inferiors del frame

  • (0,0) –> (c, e)
  • (319, 0) –> (d, e)
  • (239, 0) –> (239, 0)
  • (319, 239) –> (319, 239)

Després d’algunes proves, trio els valors e = 40, d = 199 i, per simetria, c = 319 – d = 120.

Horitzontalment, la transformació serà un escalat lineal depenent de l’alçada dins del trapezi.

A l’eix vertical la densitat del mapeig ha d’anar creixent a mida que pugem pel trapezi. És a dir, a mida que ens apropem al costat superior del trapezi han d’haver-hi cada cop “més línies”.

Combinant aquestes dues transformacions s’aconsegueix fer les lletres cada cop més petites a mida que “van pujant”.

Donat un punt del frame (x0, y0) caldrà, doncs, determinar a quina posició vertical es desplaça yt i, amb aquesta dada, determinar quin escalat horitzontal li correspon yt.

És dir, una mapeig del punt (x0, y0) al punt (xt, yt)

T:(x0, y0)——> (xt, yt)

Aquesta transformació la resolc amb la següent funció

def transform(x0, y0):
     a = 319.0
     b = 239.0
     d = 199.0
     e = 40.0
     c = a - d

     # lineal
     # y1 = e + (y0 * ((b - e) / b))
     
     # parabolic
     c0 = (b - e) / (b * b)
     c1 = e
     y1 = (c0 * y0 * y0) + c1


     x2 = (((y1 * c) - (b * c)) / (e - b))
     x3 = a - x2
     x1 = x2 + (((x3 - x2) / a) *  x0)

     return (x1, y1)

El mapeig en vertical es fa amb

# lineal
# y1 = e + (y0 * ((b - e) / b))

# parabolic
c0 = (b - e) / (b * b)
c1 = e
y1 = (c0 * y0 * y0) + c1

Al codi està comentada la línia que caldria per a fer un mapeig lineal. És dir, amb “densitat” de línies homogènia.

Si proveu el mapeig vertical lineal veureu que les lletres mantenen l’alçada durant tot el seu recorregut per la pantalla.

El que aplico és el mapeig “parabòlic”, en comptes de lineal.

La següent línia defineix una paràbola amb el vèrtex (0, c1), on c1 és 40.

y1 = (c0 * y0 * y0) + c1

El valor de c0 es determina amb la següent línia

c0 = (b - e) / (b * b)

Amb aquesta definició de c0, quan y0 és b (que té el valors de 239, és dir, última línia) y1 val b, com es pot comprovar fàcilment.

C0 és molt petit (està dividit pel quadrat de 239), de forma que amb valors “petits” de y0 tinc valors petits de y1.

El resultat és que n línies y0 es mapegen a la mateixa línia y1 (n > 1)

Però el creixement parabòlic fa que a mida que creix y0, en el mapeig d’n a 1 la n  cada cop es fa més petita.

De fet, a mida que y0 s’apropa al valor de 239 (la línia inferior) la n passa a ser fraccionària. Caldrà tenir-ho en compte mes tard.

He fet servir una funció “parabòlica” per a augmentar la densitat de línies prop de la línia superior, però és podria provar una altre funció que donés un resultat similar, a veure quin efecte té.

Un cop tinc la y1, aleshores puc calcular l’escalat horitzontal. En aquest cas es tracta d’una escalat lineal.

x2 = (((y1 * c) - (b * c)) / (e - b))
x3 = a - x2
x1 = x2 + (((x3 - x2) / a) *  x0)

A partir de y1 es calcula el punt x2 fent servir l’equació de la recta que passa per (0,239) i (c=120, e=40).

Per simetria horitzontal es calcula x3.

El punt x1 es calcula considerant el mapeig lineal entre (0, 319) i (x2, x3)

9 Optimització i línies en blanc

El mapeig és el mateix per a tots els frames. Aleshores, en comptes de recalcular el mapeig per a cada frame, es pot calcular un cop al començament i mantenir-lo en un array.
Un array, en principi, de 320×240. Però el cas és que el creixement en y1 de la funció de mapeig entre els punts d’un frame (x0,y0) i els punts del trapezi (x1, y1) fa que per a increments d’1 píxel en la variació de y0 el trapezi tingui línies buides a la seva part inferior, o sigui, y1 té “forats”. La solució és fer que els increments en y0 siguin més petits que un píxel, per a que y1 no tingui forats. Fent algunes proves, he vist que amb increments de y0 de 0.5 ja n’hi ha prou per a que la funció de transformació no deixi línies buides.

Al final, doncs, el precàlcul de la transformació es pot fer amb el següent codi:

# create frame transform master
num_steps = 2.0
frame_transform = [["1" for j in xrange(0,int(num_steps))] for i in range(0, w * h_frame)]
for y in xrange(0, h_frame):
    for x in xrange(0, w):
        for inc_y in xrange(0, int(num_steps)):
            frame_transform[x + y * w][inc_y] = transform(x, y + inc_y * (1 / num_steps) )

10 Generació dels frames processats (i afegir el color)

La generació dels frames processats és directa: per a cada frame…

for frame_count in xrange(0, h_img_base - h_frame): 
      filename_frame = filename_pattern % frame_count
      filename_processed_frame = filename_processed_pattern % frame_count

      # read and transform frame 
      with open(frames_folder + filename_frame, "r") as fr:
          # discards three first header lines 
          fr.readline()
          fr.readline()
          fr.readline()

… aplico la transformació,tenint en compte que els increments de y0 seran fraccionaris. Per simplificar, en comptes de generar directament el frame transformat faig servir un array de 320×240 com a pas intermig…

# initialize black frame
frame_bits = ["1" for i in xrange(0, w * h_frame)]

for y in xrange(0, h_frame):
    for x in xrange(0, w):
        bit = fr.read(1)
        if bit == '\n':
            bit = fr.read(1)

        # parabolic. trick for filling gaps
        for inc_y in xrange(0, int(num_steps)):
            (xt, yt) = frame_transform[x + y * w][inc_y]
            if int(xt) < w and int(yt)< h_frame:
                frame_bits[int(xt) + int(yt) * w] = bit

Finalment, escric el frame transformat. Aprofito aquest últim pas per afegir el color ja que durant tot el procés he considerat la imatge només en blanc i negre.

Simplement, genero el frame transformat amb el format PPM

10.1 Format PPM

De la viquipèdia, un exemple de PPM.

P3
# The P3 means colors are in ASCII, then 3 columns and 2 rows,
# then 255 for max color, then RGB triplets
3 2
255
255   0   0     0 255   0     0   0 255
255 255   0   255 255 255     0   0   0

Per tant,

# write transformed frame
# c = 0
rgb = ""
black = " 0 0 0"
yellow = " 255 255 0"  
print "[Processed Frame %0#5d]" % frame_count
with open(processed_frames_folder + filename_processed_frame, "w") as fw:
    fw.write("P3\n")
    fw.write("# CREATOR: Albert Baranguer Codina\n")
    fw.write("%d %d 255\n" % frame_size)
    for bit in frame_bits:
        if bit == "1":
            rgb = black
        else:
            rgb = yellow 
        fw.write(rgb)
        c = c + 1
        if (c == w):
            c = 0
            fw.write('\n')

El resultat de l’execució d’aquest codi és que tindré 560 frames processats a carpeta processed-frames

11 generació del video mp4

Arribats a aquest punt ja podem visualitzar el vídeo fent servir una petita modificació del player1.py que he mostrat abans. Però com que el resultat demanat era un mp4, amb un parell de passos addicionals fent servir convert de imagemagick i ffmpeg obtinc el resultat final.

La idea original era fer servir ffmpeg amb els pbm generats en el pas anterior per a obtenir el vídeo, pero en proves he vist que al ffmpeg no sembla agradar-li aquest format de imatge. O sigui que he transformat els pbm a png amb el següent script: pbm-to-png.sh

#!/bin/bash

for filename in $(ls -b ./processed-frames)
do
    convert ./processed-frames/"$filename" ./png/"$filename".png
done
echo "done!"

I un cop he obtingut els frame en format png a la carpeta png, finalment, he generat l’mp4 amb create-mp4.sh

#!/bin/bash
ffmpeg -i png/frame_%05d.processed.pbm.png \
       -c:v libx264 \
       -pix_fmt yuv420p png/starwars-credits-fx.v1.mp4

I vet aquí el resultat

 

12 Repositori a GitHub

Podeu trobar el codi ue he fet servir al meu repositori de GitHub

https://github.com/abaranguer/sw-rolling-fx

“Revolution OS”, the movie.

És estiu i és moment de relaxar-se.  Avui toca cine: El documental “Revolution OS”.

Vet aquí la ressenya de la pel·lícula que es pot trobar al YouTube:

“Revolution OS is a 2001 documentary which traces the history of GNU, Linux, and the open source and free software movements.

It features several interviews with prominent hackers and entrepreneurs (and hackers-cum-entrepreneurs), including Richard Stallman, Michael Tiemann, Linus Torvalds, Larry Augustin, Eric S. Raymond, Bruce Perens, Frank Hecker and Brian Behlendorf.

The film begins in medias res with an IPO, and then sets the historical stage by showing the beginnings of software development back in the day when software was shared on paper tape for the price of the paper itself.

It then segues to Bill Gates’s Open Letter to Hobbyists in which he asks Computer Hobbyists to not share, but to buy software. (This letter was written by Gates when Microsoft was still based in Arizona and spelled “Micro-Soft”.)

Richard Stallman then explains how and why he left the MIT Lab for Artificial Intelligence in order to devote his life to the development of free software, as well as how he started with the GNU project.

Linus Torvalds is interviewed on his development of the Linux kernel as well as on the GNU/Linux naming controversy and Linux’s further evolution, including its commercialization.

Richard Stallman remarks on some of the ideological aspects of open source vis-á-vis Communism and capitalism and well as on several aspects of the development of GNU/Linux.

Michael Tiemann (interviewed in a desert) tells how he met Stallman and got an early version of Stallman’s GCC and founded Cygnus Solutions.

Larry Augustin tells how he combined the resulting GNU software and a normal PC to create a UNIX-like Workstation which cost one third the price of a workstation by Sun Microsystems even though it was three times as powerful. His narrative includes his early dealings with venture capitalists, the eventual capitalization and commodification of Linux for his own company, VA Linux, and ends with its IPO.

Frank Hecker of Netscape tells how Netscape executives released the source code for Netscape’s browser, one of the signal events which made Open Source a force to be reckoned with by business executives, the mainstream media, and the public at large.

(this text is available under the terms of the GNU Free Documentation License)

I sense més dil·lació, amb tots vosaltres els gurús i hackers que són a l’origen del Linux, del Programari Lliure (el “Free Software”, amb “free as in freedom”) , del sistema GNU i del Free and Open Source Software (FOSS).

Apaguem els llums. “Revolution OS”, the movie:

Cinc problemes de programació

Una de les conseqüències de l’ERO a Indra (on encara treballo) és que a …curt? …mig? termini m’incorporaré al mercat laboral, o el que és el mateix, que hauré de buscar feina. I és bastant probable (i em sembla el més sensat) que la feina que busqui estigui relacionada amb el que sé fer, que és, bàsicament, de programador.

Programador? Alguns diran Analista, o en anglès, Software Engineer, o Software Architect, o títols encara més originals… En essència, però, jo crec que el que és distintiu de la meva feina és que sóc capaç de programar ordinadors i fer-los funcionar junts. Per tant, fem-ho fàcil i digues-me programador. Ras i curt.

El cas és que buscant sobre el que em puc esperar a les entrevistes de feina vaig trobar el següent article: “Five programming problems every Software Engineer should be able to solve in less than 1 hour“. Sí,ho heu entès: cinc problemes de programació que tot enginyer de software hauria de ser capaç de resoldre en menys d’una hora. Em va semblar força interessant.

L’autor d’aquest article diu que quan publica una oferta demanant programadors es troba amb candidats que no saben què vol dir “programar”. Aleshores proposa el que, al seu entendre, són cinc problemes bàsics que tot programador “de veritat” hauria de ser capaç de resoldre en menys d’una hora.

Cinc problemes en una hora. Cal fer algunes consideracions. Què ha de fer un programador per superar un repte d’aquest estil? primer de tot, cal comprendre (analitzar) el problema i les seves implicacions, i dissenyar una solució. Encara que sigui en forma de notes mentals. Hi ha alguna forma de validar el disseny de la solució trobada? Un cop analitzat i dissenyat el problema, cal pensar en la implementació de la solució. Probablement serà més eficient un llenguatge específic del domini del problema que un llenguatge de propòsit general. Si es pot triar, en general és més ràpid desenvolupar una resposta amb un intèrpret que amb un compilador (ni que només sigui perquè s’estalvia el temps de compilat); per descomptat, és més eficient disposar d’un bon entorn de desenvolupament, que proporcioni coses com compleció del codi, acoloriment de sintaxi, snippets de codi, generació automàtica de codi, depurador… que fer servir un editor de texts pelat, per molt que per Internet es trobin acudits tan graciosos com aquell que diu que el millor IDE és vi + gcc.

Per descomptat, més enllà de si interpretat o compilat, és clau per a desenvolupar de forma eficient, el conèixer a fons el llenguatge triat per al desenvolupament. Finalment, l’experiència és un grau. És més probable que algú que ha fet moltes hores de programació s’hagi trobat amb problemes similars en algun moment de la seva trajectòria i que els hagi solucionat. En resum: per a millorar l’eficiència cal experiència i coneixement en el llenguatge i les eines a utilitzar.

A més de tot l’anterior, un programador de veritat sap que l’objecte del seu treball, el codi, ha de tenir unes determinades característiques, com per exemple la legibilitat i l’organització. per no parlar de la gestió de recursos, erros i la generació i gestió de casos de proves.

Per si fos poc, el codi, com producte, s’ha d’administrar i segueix uns fluxos dins de les organitzacions. Al menys com usuari, un programador hauria d’estar al cas dels sistemes de Control de Versions, o dels sistemes d’Integració Continua.

Tot això son coneixements tècnics que ha de tenir el programador. Però no només. El programador hauria de tenir unes competències generals que inclourien la capacitat d’auto-organització (tècniques com GTD), l’autonomia, l’auto-didactisme, la bona capacitat de comunicació, oral i escrita (afegiria també audiovisual), la capacitat de treball en equip, i també capacitat didàctica i de lideratge. I segur que em deixo coses.

Bé. De totes aquestes característiques no no en parla l’autor de l’article. Jo trobo que a l’hora de contractar un programador caldria tenir-les en compte. Però el fet és aquest: el que de debò és imprescindible per a un programador és que sàpiga programar.

Tornant al prinicipi: Quins són aquests cinc problemes que tot programador hauria de poder resoldre en menys d’una hora?

Problem 1
Write three functions that compute the sum of the numbers in a given list using

  • a for-loop
  • a while-loop
  • recursion

Problem 2
Write a function that combines two lists by alternatingly taking elements. For example: given the two lists [a, b, c] and [1, 2, 3], the function should return [a, 1, b, 2, c, 3].

Problem 3
Write a function that computes the list of the first 100 Fibonacci numbers. By definition, the first two numbers in the Fibonacci sequence are 0 and 1, and each subsequent number is the sum of the previous two. As an example, here are the first 10 Fibonnaci numbers: 0, 1, 1, 2, 3, 5, 8, 13, 21, and 34.

Problem 4
Write a function that given a list of non negative integers, arranges them such that they form the largest
possible number. For example, given [50, 2, 1, 9], the largest formed number is 95021.

Problem 5
Write a program that outputs all possibilities to put + or – or nothing between the numbers 1, 2, …, 9 (in this order) such that the result is always 100. For example: 1 + 2 + 34 – 5 + 67 – 8 + 9 = 100.

Déu n’hi do! Els problemes 1,2 i 3 són senzills. Ara bé, el 4 i el 5 poden fer-te rumiar una bona estona.

En una actualització de post, el mateix autor de l’article reconeixia que els problemes 4 i 5 havien generat una important controvèrsia.

Bé, jo també trobo que en una hora els cinc problemes és, més aviat, justet.

En el meu cas, em vaig decidir a fer els problemes. Ara bé, no pretenia resoldre’ls en una hora. De fet, el que pretenia és que em servissin per practicar un llenguatge que em fa molta gràcia i que he “descobert”, per dir-ho d’alguna manera, fa poc: amb l’Elisp de l’Emacs.

Sí, he fet servir lisp per resoldre els problemes, què passa? xD (de fet, el problema 5 l’he fet primer amb Python, però ja en parlaré després)

Allà van les meves solucions:

Solució al problema 1 (amb for-loop –> dotimes)

;; The 5 problems
;;
;; Problem 1
;; Write three functions that compute the sum of the numbers in a given list using 
;;  - a for-loop
;;  - a while-loop
;;  - recursion
;;
;; 1.1 - for-loop (dotimes)
(progn 
  (setq nums (list 1 2 3 4 5 6 7 8 9 10))
  (setq N (length nums))
  (setq suma 0)

  (dotimes (i N) 
    (setq suma (+ suma (elt nums i)))
  )

  (insert (format "\nFor loop (with dotimes) sum: %d" suma))
)
For loop (with dotimes) sum: 55

Solució al problema 1 (amb while-loop)

;; 1.2 - while
(progn 
  (setq nums (list 1 2 3 4 5 6 7 8 9 10))
  (setq N (length nums))
  (setq suma 0)
  (setq i 0)
  (while (< i N) 
    (setq suma (+ suma (elt nums i)))
    (setq i (1+ i))
  )

  (insert (format "\nWhile loop sum: %d" suma))
)
While loop sum: 55

Solució al problema 1 (recursiva)

;; 1.3 - recursion
(defun recursiveSum(numsList)
  (setq N (length numsList))
  
  (if (> N 0)
    (+ (car numsList) (recursiveSum (cdr numsList)))  ;; if part
    0                                                 ;; else part
  )
)

(progn 
  (setq nums (list 1 2 3 4 5 6 7 8 9 10))
  (setq suma 0)

  (setq suma (+ (car nums) (recursiveSum (cdr nums)))) 
  
  (insert (format "\nRecursive sum: %d" suma))
)
Recursive sum: 55

Fàcils, no? Anem pel problema 2:

;; Problem 2
;; Write a function that combines two lists by alternatingly taking elements. 
;; For example: given the two lists [a, b, c] and [1, 2, 3], 
;; the function should return [a, 1, b, 2, c, 3].
;;
(defun merger (listData indexListData listUnion) 
  (push (nth indexListData listData) listUnion)
  listUnion   ;; not strictly necessary
)

(progn 
  ;; test data
  (setq list1 (list "a" "b" "c"))
  (setq list2 (list 1 2 3 4 5 6))
  (setq listUnion (list))

  ;; get length of lists
  (setq lengthList1 (length list1))
  (setq lengthList2 (length list2))
  
  ;; length of union list
  (setq lengthUnion (+ lengthList1 lengthList2))

  (setq indexList1 0)
  (setq indexList2 0)

  (dotimes (i lengthUnion)
    ;; list 1
    (if (< indexList1 lengthList1)
      (setq listUnion (merger list1 indexList1 listUnion)) 
    )
    (setq indexList1 (1+ indexList1))
    
    ;; list 2
    (if (< indexList2 lengthList2)
      (setq listUnion (merger list2 indexList2 listUnion)) 
    )
    (setq indexList2 (1+ indexList2))
  )
 
  ;; reverse
  (setq listReversed (reverse listUnion))

  ;; show results
  (print "List merger")
  (print listReversed)
)

Una mica embolicat, no? No és el millor codi que he escrit, la veritat. El codi s’hauria simplificat si en comptes de fer servir list hagués fet servir vectors.

La funció merger no fa mes que posar al principi de la llista unificada l’element enèsim de la llista passada com argument. Com que els elements de les dues llistes es van posar alternativament al començament (push) de la llista unificada, per això cal fer un reverse de la llista unificada al final.

Bé, amb Python o amb Basic m’hauria sortit millor. Seguim.

Solució del problema 3

;; Problem 3
;; Write a function that computes the list of the first 50 Fibonacci numbers. 
;; By definition, the first two numbers in the Fibonacci sequence are 0 and 1, 
;; and each subsequent number is the sum of the previous two. 
;; As an example, here are the first 10 Fibonnaci numbers: 
;; 0, 1, 1, 2, 3, 5, 8, 13, 21, and 34.
;; 
(progn 
  (setq n0 0.0)
  (setq n1 1.0)
  (insert "\n - 50 first Fibonacci numbers -\n")
  (insert "0\n1\n")
  (dotimes (i 50)
    (setq n (+ n1 n0))
    (insert (format "%d\n" n))
    (setq n0 n1)
    (setq n1 n)
  )
)
 - 50 first Fibonacci numbers -
0
1
1
2
3
5
8
13
21
34
...
701408733
1134903170
1836311903
2971215073
4807526976
7778742049
12586269025
20365011074

Un clàssic. La successió de Fibonacci. Aquest problema també és molt senzill.

Solució (errònia) al problema 4

;; Problem 4
;; Write a function that given a list of non negative integers, arranges them such that they form the largest possible number. 
;; For example, given [50, 2, 1, 9], the largest formed number is 95021.
;; 
(defun greater (lower upper)
  (and (not (string< lower upper)) (not (string= lower upper)))
)

(defun swap (row i)
   (setq aux (aref row i))
   (aset row i (aref row (1+ i)))
   (aset row (1+ i) aux)
)

(defun bubble-sort (strNums)
  (setq N (length strNums))
 
   ;; sort array of strings 
  (dotimes (j (- N 1))
    (dotimes (i (- N 1)) 
      (setq lower (aref strNums i))
      (setq upper (aref strNums (1+ i)))
   
      (if (greater lower upper)
        (swap strNums i)
      )
      ;;(show-row strNums)
    )
  )
)

(defun show-row (strNums)
  (setq N (length strNums))
  (dotimes (i N)
    (insert (format "%s " (aref strNums i )))
  )
  (insert "\n") 
)

(defun reverseVector (row)
  (setq N (length row))
  (dotimes (j (/ N 2))
    (setq val1 (elt row j))
    (setq val2 (elt row (- (- N 1) j)))
      
    (aset row (- (- N 1) j) val1)
    (aset row j val2)
  ) 
)

(defun concatVector (row)
  (setq N (length row))
  (setq retvalue "")
  (dotimes (i N)
    (setq retvalue (concat retvalue (elt row i)))
  )
  retvalue  ;; not strictly necessary 
)

(progn
  ;; data: list of non negative integers
  (setq nums (list 9 8 7 6 5 4 3 2 1 0))
 
  ;; convert to array of strings
  (setq N (length nums))
  (setq strNums (make-vector N nil))
  (dotimes (i N) 
    (aset strNums i (number-to-string (elt nums i)))
  )

  (insert "\nGreatest integer\n")
  (insert "\nunsorted array:\n")  
  (show-row strNums)

  (insert "\nsorted array:\n") 
  (bubble-sort strNums)
  (show-row strNums)

  (insert "\nreversed sorted array:\n") 
  (reverseVector strNums)
  (show-row strNums)
 
  ;; string to number
  (setq greatestNumber (string-to-number (concatVector strNums)))
  (insert (format "Greatest integer: %d\n" greatestNumber)) 
)
Greatest integer

unsorted array:
9 8 7 6 5 4 3 2 1 0 

sorted array:
0 1 2 3 4 5 6 7 8 9 

reversed sorted array:
9 8 7 6 5 4 3 2 1 0 
Greatest integer: 9876543210

Doncs bé, resulta que la solució no és correcta! He suposat que n’hi havia prou amb ordenar (funció bubble-sort, com no!) alfabèticament els números, però resulta que aquesta suposició és falsa, com indiquen en aquest altre post:

“A lot of folks have rightly pointed out that this problem can be solved using a lexical comparison of strings. However, that’s not all you need.

Consider the following example: [5, 50, 56]. A lexical comparison returns 56, 50, and 5, but that doesn’t make the larger number (56, 5, 50 does)”

O sigui que: meeec! cal rumiar un altre cop la solució del problema 4. Queda pendent la solució per a un futur post.

UPDATE: Solució Brute Force:

Ahir no estava d’humor per a posar-me a investigar un algorisme pera solucionar el problema 4… i avui tampoc! L’opció és fer força bruta, que,de fet, als ordinadors se’ls dona molt bé.

Amb força bruta i generadors de Python (itertools) la cosa queda tan simple com això:

#!/usr/bin/env python
# -*- coding: utf-8 -*-
#
# biggetst integer

import itertools as itt

print "\nBiggest number\n"

nums = ['500', '5', '51', '52', '504', '506', '54', '56', '514', '515', '516', '575']

max_str = ''
num_iters = 0

for perm in itt.permutations(nums, len(nums)):
    num_iters = num_iters + 1
    num = ''.join(perm)
    if  num > max_str:
        max_str = num

print "max: %s; iters: %d\n" % (max_str, num_iters)        

'''
>>> import biggest

Biggest number

max: 575565545251651551514506504500; iters: 479001600
''' 

Aquesta solució és qualsevol cosa menys elegant, però és molt ràpida de programar i soluciona el problema en un temps finit. Cal dir que he tirat, de nou, de la màgia de Pyhton que m’ha permès resoldre amb el simple import de les itertools la generació de les permutacions dels números. Després la concatenació amb el join i actualitzar el màxim a mida que es va trobant.

Queda pendent, per a quan tingui temps i ganes d’investigar-ho, el trobar un algorisme més intel·ligent que la solució per força bruta. Això sí, en tot cas, la força bruta sempre és una opció a considerar (i els bons programadors ho saben).

Finalment, la solució al problema 5

Primer la solució amb Python. I ara veureu perquè Python és el Llenguatge dels Déus:

#!/usr/bin/env python
# -*- coding: utf-8 -*-
'''
Problem 5
Write a program that outputs all possibilities to put + or - or nothing 
between the numbers 1, 2, ..., 9 (in this order) 
such that the result is always 100. 
For example: 1 + 2 + 34 - 5 + 67 - 8 + 9 = 100.
'''
def get_base3_indexes(num):
    ix = [0,0,0,0,0,0,0,0]
    done = False
    i = 0
    while not done:
        quot, mod = num / 3, num % 3

        if quot >= 3:
            num = quot
            ix[i] = mod
        else:
            done = True
            ix[i] = mod
            ix[i + 1] = quot

        i = i + 1

    ix.reverse()
    return ix

if __name__ == "__main__":
    symbols = ["+","-"," "]
    formula = "1%c2%c3%c4%c5%c6%c7%c8%c9"
    ix = [0,0,0,0,0,0,0,0]
     
    # és a dir: Variacions amb Repetició de 3 elements agafats de 8 en 8
    # RV n m = n ** m
 
    top =  3**8
    
    for i in range(0, top - 1):
        ix = get_base3_indexes(i)
        
        calc = formula % (symbols[ix[0]],
                          symbols[ix[1]],
                          symbols[ix[2]],
                          symbols[ix[3]],
                          symbols[ix[4]],
                          symbols[ix[5]],
                          symbols[ix[6]],
                          symbols[ix[7]])

        calc2 = [c for c in calc if c != " "]
        calc2_compact = "".join(calc2)
        result = eval(calc2_compact)
        if result == 100:
            print "%s = %d" % (calc2_compact, result)
            
    print "%d lines analyzed" % top
    print "done!"

O sigui, calcula les “variacions amb repetició de tres elements agafats de 8 en 8″ Els tres elements són les tres “operacions” possibles entre dígits: sumar (“1” + “2” = 3), restar (“1” – “2” = -1) i concatenar (“1” concat “2” = “12”) i els agafo de 8 en 8 perquè són 8 posicions per a operadors entre els 9 operands numèrics. la iteració per tots els casos possibles i la conversió a base 3 de l’index actual permet posar els operadors adequats on els toca en cada iteració.

Però la màgia de Python ve en aquestes tres línies:

        calc2 = [c for c in calc if c != " "]
        calc2_compact = "".join(calc2)
        result = eval(calc2_compact)

La primera línia fa una llista d’elements (calc2) a partir de la llista original (calc) on els operadors “concat” apareixen com blancs entre els números. Agafa cada element c de calc excepte els elements iguals a ” “. És dir, “compacta” la llista calc eliminant-li els blancs. Es tracta d’un bonic exemple de Python Comprehensions.

La següent línea genera un string a partir de la llista compactada.

la tercera línia avalua la suma.

La dada és que fa tot això en tres línies. Eficiència i productivitat en un llenguatge és exactament això.

I ara l’equivalent amb elisp:

;; Problem 5
;; Write a program that outputs all possibilities to put + or - or nothing 
;; between the numbers 1, 2, ..., 9 (in this order) 
;; such that the result is always 100. 
;; For example: 1 + 2 + 34 - 5 + 67 - 8 + 9 = 100.
;; 
;; variations with repetition of 3 elements (the +,- and "concat" operators) 
;; taken 8 (8 operands within the 1,2,3,4,5,6,7,8 and 9 operands) at a time
;;
;; VR n r = n ** r
;;  
(defun main ()
  ;; return vector with coeficients of the conversion of num to base 3.
  (defun get_base3 (num)
    (let
      ( 
        (base3 (make-vector 8 0))
        (n 8)
        (done nil)
        (i 0)
        (quot 0)
        (mod 0)
      )
      
      (while (not done)
        (setq quot (/ num 3))
        (setq mod (% num 3))

        (if (>= quot 3)
          (progn
            (setq num quot)
            (aset base3 i mod)
        
          )
          (progn
            (setq done t)
            (aset base3 i mod)
            (aset base3 (1+ i) quot)
          ) 
        )
        (setq i (1+ i))
      )

      ;; return
      base3
    )
  )

  ;; clean white spaces
  (defun clean_white_spc (str)
    (let
      ( 
        (c "")
        (j 0)
        (str_out "")
        (len (length str))
      )
      (dotimes (j len)
        (setq c (substring str j (+ j 1)))
        (if (not (string= c " "))
          (setq str_out (concat str_out c))
        )
      )

      ;; return
      str_out
    )
  )

  ;; formula parser
  (defun perform_calculation (formula)
    (let 
      (
        (n (length formula))
        (stackoperands (make-vector 9 0))
        (stackoperators (make-vector 8 "+"))
        (i 0)
        (j 0)
        (k 0)
        (accum 0)
      )
 
      ;; lexical parser
      (dotimes (i n)
        (setq c (substring formula i (1+ i)))
        (if (and (not (string= c "+")) (not (string= c "-")))
          (progn ;; true. c is a number
            (setq accum (+ (* accum 10) (string-to-number c)))
          ) 
          (progn ;; false. c is an operator  
            (aset stackoperands j accum)
            (aset stackoperators k c)
            (setq k (1+ k))
            (setq j (1+ j))
            (setq accum 0)
          )
        )
        (if (> accum  0)
          (aset stackoperands j accum)
        )
      )

      ;; performs the sum
      (setq i 0)
      (setq j 0)
      (setq operator "")
      (setq operand 0)
      (setq accum 0)

      (dotimes (i (length stackoperands))
        (setq operand (aref stackoperands i))
        (if (= i 0)
          (progn ;; i = 0
            (setq accum operand) 
          )
          (progn ;; i > 0
            (setq j (1- i))
            (setq operator (aref stackoperators j))
            (if (string= operator "+")
              (setq accum (+ accum operand))
              (setq accum (- accum operand))
            )
          )
        )
      )

      ;; return
      accum
    )
  )

  ;; main
  (let
    (
      (symbols ["+" "-" " "])
      (formula "1%s2%s3%s4%s5%s6%s7%s8%s9")
      (ix (make-vector 8 0)) 
      (top (- (expt 3 8) 1))
      (i 0)
      (count 0)
    )
  
    (insert "\nSum 100\n")
 
    (dotimes(i top) 
      (setq ix (get_base3 i))
      (setq calc (format formula (aref symbols (aref ix 0))
                                 (aref symbols (aref ix 1))
                                 (aref symbols (aref ix 2))
                                 (aref symbols (aref ix 3))
                                 (aref symbols (aref ix 4))
                                 (aref symbols (aref ix 5))
                                 (aref symbols (aref ix 6))
                                 (aref symbols (aref ix 7))  
                 )
      )
     
      (setq calc2 (clean_white_spc calc))
      (setq sum (perform_calculation calc2))

      (if (= sum 100)
        (progn 
          (setq count (1+ count))
          (insert (format "%d: %s = %d\n" count calc2 sum))
        )
      )
    )
    (insert (format "\n%d lines analyzed\ndone!"  top))
  )
)

És equivalent a la solució amb Pyhton, només que ha calgut fer funcions per a fer la compactació i eliminació dels espais en blanc, i també per avaluar la fórmula amb un senzill parser.

……

Una hora diu. En fi.

Dia de la Llibertat del Programari al Centre Cívic de Les Corts (BCN)

Avui, dissabte 21 de setembre, és el Software Freedom Day, Dia de la Llibertat del Programari, en català. A Barcelona, com ve sent costum, per aquest motiu es realitzaran diverses conferències i activitats al centre Cívic de les Corts organitzades per Caliu, el Catalan Linux Users goup. Wiki del DLP.

També al Prat de Llobregat es fan actes a l’Uikú Coworking (facebook).

Un altre dels llocs “calents” va ser ¡, ahir, Santa Coloma de Cervelló on “La Komfraria de la Vila del Pingüí” fa diverses activitats avui. Vet aquí el programa. Els “komfrares” fan l’acte un dia abans per a poder participar avui als actes de Barcelona.

És una trobada d’activistes i gent interessada en el programari lliure. Vet aquí la nota de premsa (de la wiki del DLP)que anuncia la diada:

DIA DE LA LLIBERTAT DEL PROGRAMARI A CATALUNYA

“El proper dissabte 21 de setembre se celebra a més de 270 països de tot el món el Dia de la Llibertat del Programari o Software Freedom Day, diada que compta amb lideratge de la Digital Freedom Foundation i el suport d’empreses i organitzacions com Google, Canonical, la Free Software Foundation, Joomla!, FreeBSD o Creative Commons, entre d’altres. En aquesta edició hi haurà sis esdeveniments a Espanya, repartits entre Catalunya (2), Madrid (1), Andalusia (2) i Galícia (1).

La celebració que s’inicià l’any 2004, es realitza any rere any a Catalunya des del 2005 de la mà de Caliu[3], i l’any 2011 s’hi va afegir la Konfraria[4] permetent que la cel·lebració s’allargués en el temps. Com cada any a Catalunya ho celebrarem, i aquest cop començarem divendres 20 per la tarda a La Palma de Cervelló, al Teatre Societat Aliança Palmarenca, i continuarem dissabte 21 a Barcelona, al Centre Cívic de Les Corts. S’ofereixen xerrades, tallers, festa d’instal·lació on usuaris avançats i experts en GNU/Linux oferiran ajuda en instal·lacions de programari lliure i respondran dubtes. Consulteu els enllaços per veure les activitats programades a cada lloc.

El divendres 20 de setembre començarà la celebració a la Palma de Cervelló (Baix Llobregat) a partir de les 17h de la tarda i en diferents indrets: Teatre Aliança Palmarenca, PuntTIC i Biblioteca Municipal (on recentment han migrat els ordinadors del telecentre al sistema operatiu lliure Ubuntu) amb tallers, xerrades i demostracions. La jornada conclourà amb un sopar de germanor. La cel·lebració continuarà el dissabte 21 de setembre a Barcelona al Centre Cívic de les Corts, amb un conjunt de xerrades diverses que ocuparan tota la jornada des de les 10h del matí i fins a les 19h de la tarda. Usuaris avançats i experts en GNU/Linux oferiran ajuda en instal·lacions de programari lliure i respondran dubtes.

Podeu veure totes les activitats a: http://wiki.caliu.cat/index.php/DLP2013 i http://www.konfraria.org/dlp2013/
El Dia de la Llibertat del Programari és una celebració mundial del programari lliure. L’objectiu d’aquesta celebració anual és d’educar el públic general sobre els beneficis d’usar programari lliure d’alta qualitat en l’educació, el govern, a casa, i a les empreses, és a dir, arreu! Imagineu que, després d’uns pocs anys, la tesi que tant de temps us va portar de fer no fos llegible, o que els vostres vídeos familiars no es puguin veure. Si us queixeu a l’empresa de programari, us diran que us gasteu encara més diners en una «actualització», que només resulta ser més lenta i amb més defectes que l’anterior versió. Fins i tot un nou reproductor de música, només per trobar que refusa la música que abans podíeu escoltar en el reproductor antic? Malauradament, la majoria de gent viu en aquest món avui.

El Dia de la Llibertat del Programari existeix per mostrar el públic en general que hi ha una manera de sortir d’aquest cercle viciós. Mitjançant l’ús del programari lliure, obteniu el control sobre el vostre ordinador i les vostres dades. Cada persona té la llibertat de participar i d’usar el programari lliure, ja sigui usant un sistema operatiu completament lliure com el GNU/Linux o una plataforma no-lliure com Windows o Mac OS usant-hi programes lliures.

El Programari Lliure es defineix per les següents llibertats:

0.- La llibertat d’utilitzar el programari per a qualsevol ús.
1.- La llibertat d’estudiar el funcionament del programari, i adaptar-lo a les nostres necessitats. L’accés al codi font és necessari per tal de gaudir d’aquesta llibertat.
2.- La llibertat de redistribuir les còpies.
3.- La llibertat de millorar el programari i de distribuir aquestes millores, de manera que tothom se’n pugui beneficiar. L’accés al codi font és necessari per tal de gaudir d’aquesta llibertat.

Aquestes quatre llibertats que Richard Stallman va escriure l’any 1986 permeten la participació igualitària en l’era de la informació. Un bon exemple n’és la presència de llengües minoritàries en moltes aplicacions de programari lliure.”

#dlp2013

LinuxCon Europe 2012. Del 5 al 9 de novembre a l’Hotel Fira Palace de Barcelona.

Durant la setmana del 5 al 9 de novembre es celebrarà a Barcelona, a l’hotel Fira Palace del carrer Lleida, la LinuxCon Europe 2012.

Per a més informació, podeu vistar la pàgina oficial de la LinuxCon.

La LinuxCon és una conferència en la que acostumen a participar alguns noms importants o populars del món Linux. Per exemple, en aquesta ocasió es comptarà amb Mark Shuttleworth, el fundador de Canonical, l’empresa que impulsa les distribucions Ubuntu. Però no només amb ell. També hi seran altres actors importants, com Red Hat, SuSE, Intel, o EverNote. A la conferència es presentaran els plans dels diferents actors pel que fa al futur de Linux.

Principals oradors:

Dave Engberg Dave Engberg
Evernote
Ralf Flaxa Ralf Flaxa
SUSE
Catarina Mota Catarina Mota
Open Materials
Marten Mickos Marten Mickos
Eucalyptus
Mark Shuttleworth Mark Shuttleworth
Canonical
Imad Sousou Imad Sousou
Intel
Brian Stevens Brian Stevens
Red Hat

De la pàgina oficial:

“LinuxCon Europe 2012

November 5 – 9, 2012 · Hotel Fira Palace · Barcelona, Spain

LinuxCon Europe will bring together the best and brightest that the Linux community has to offer, including community developers, system administrators, business executives and operations experts. LinuxCon Europe will deliver attendees top notch speaking talent (from across Europe and around the globe), innovative and abundant program content, and a wide variety of opportunities to connect with peers.

Who Should Attend:
Software Developers, Linux IT Professionals, Corporate End Users, Senior Business Executives, IT Operations Experts, System Administrators, Students, Media and anyone else with an interest in the Linux ecosystem.
Schedule of Events:

Monday, November 5th
LinuxCon Europe, ELCE, EFL Developer Day, Cloud Summit

Tuesday, November 6th
LinuxCon Europe, ELCE, Cloud Summit

Wednesday, November 7th
LinuxCon Europe, ELCE, Cloud Summit, KVM Forum, oVirt Workshop

Thursday, November 8th
Gluster Workshop, Yocto Developer Day, KVM Forum, oVirt Workshop, OpenSAF Symposium

Friday, November 9th
KVM Forum, oVirt Workshop, Wireless Summit”

Software Freedom Day 2012

Avui és el Software Freedom Day, o “Dia  de la Llibertat del Programari”,  o millor, el Dia del Programari Lliure.

Per entendre’ns: la Festa Major del Programari Lliure.

Això pot sonar una mica friqui i  segurament ho és però, com va dir algú, no jutgeu i no sereu jutjats.

El cas és que aquest dia es celebra a molts llocs arreu del món.A Catalunya també:  a casa nostra la celebració l’organitzen conjuntament Caliu i la Konfraria.

Ahir es van encetar els actes a La Palma de Cervelló; i durant la jornada d’avui hi han programats tot un seguit de conferències, xerrades i debats al Centre Cívic de Les Corts, a Barcelona.

La diada es tanca amb un sopar de germanor.

Quins són els objectius d’aquest dia? Són aquests:

(de la wiki del Caliu – SFD2012)

Objectius
  • 1. celebrar la llibertat del programari i la gent que hi ha al seu voltant
  • 2. fomentar una comprensió general de la llibertat del programari i encoratjar l’adopció del programari lliure i els estàndards oberts
  • 3. crear un accés més equitatiu a les oportunitats mitjançant l’ús de tecnologies participatives
  • 4. promoure el diàleg constructiu de responsabilitats i drets a la societat de la informació
  • 5. ser inclusius d’organitzacions i individus que comparteixen la nostra Visió
  • 6. ser pragmàtics, transparents i responsables com a organització

Una salutació i molts ànims als organitzadors. Altres anys m’hi he pogut deixar caure. Enguany no ha pogut ser. En tot cas, el que és important és fer servir Programari Lliure cada dia, estendre’n l’us i millorar-lo, si pot ser.

Feliç Diada!

Com preparar un entorn de desenvolupament per a Python for Android.

En un post anterior vaig presentar el Python for Android (Py4A), que és un python per a dispositius Android que funciona sobre l’Scripting Layer For Android (SL4A).

En aquell post presentava diversos scripts que es podien executar sobre el dispositiu Android. O sobre l’emulador d’Android que es troba disponible a l’Android SDK.

Tanmateix, en eaquell post no parlava dels entorns disponibles per a desenvolupar els scripts. Com puc desenvolupar amb Py4A de forma còmoda?

Prerequisits

Evidentment, és prerequisit tenir instal·lat a la tablet l’SL4A i el Py4A.
A l’ordinador que farem servir per desenvolupar cal tenir el JDK 1.6 de Java, com a mínim. El JDK permetrà instal·lar, a continuació l’Android SDK, el qual ens proporciona les eines que ens interessen: l’adb i el ddms. El DDMS és, en realitat, una aplicació Java.

L’Android SDK proporciona un parell d’eines que seran útils per a desenvolupar scripts Python: l’Android Debug Bridge (adb) i el Dalvik Debug Monitor Server (ddms).

Per a poder aprofitar les dues eines esmentades caldrà posar l’SL4A en “mode servidor”. Amb la combinació del “mode servidor”, l’adb, el ddms, i un cable usb serem capaços de desenvolupar scripts python al nostre ordinador i executar-los de forma immediata al dispositiu Android o a l’emulador de l’Android SDK.

Connectar el dispositiu per USB.

El que ve a continuació està basat en https://help.ubuntu.com/community/AndroidSDK

El primer pas ha estat aconseguir que l’adb i el ddms reconeixessin la meva tablet. Es tracta d’un model barat de fabricació xinesa: una tab-playtabpro de Point-of-View, que munta un sistema operatiu Android 4.0.3.

Activar depuració USB
Per a fer-ho, he connectat la tablet a l’ordinador amb el cable USB. NO he activat l’emmagatzematge USB. Quan s’activa l’emmagatzematge USB, per seguretat, hi han tot un seguit d’opcions que es desactiven a la tablet. En particular, es perd la visibilitat de sdcard. Repeteixo: NO he activat l’emmagatzematge USB. Aleshores he posat la tablet en mode de “depuració USB”. Per a fer això he anat a la configuració de la tablet, a les “opcions de desenvolupador” dins del submenú “Sistema” i allà he marcat “Depuració d’USB”, com es veu a la imatge:

ID Vendor
(de http://developer.android.com/tools/device.html)
En un terminal he executat l’ordre lsusb (estic fent les meves proves amb un Linux Lubuntu 12.04).

albert@athena:~$ lsusb
Bus 001 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub
Bus 002 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub
Bus 003 Device 001: ID 1d6b:0001 Linux Foundation 1.1 root hub
Bus 004 Device 001: ID 1d6b:0001 Linux Foundation 1.1 root hub
Bus 005 Device 001: ID 1d6b:0001 Linux Foundation 1.1 root hub
Bus 006 Device 001: ID 1d6b:0001 Linux Foundation 1.1 root hub
Bus 007 Device 001: ID 1d6b:0001 Linux Foundation 1.1 root hub
Bus 008 Device 001: ID 1d6b:0001 Linux Foundation 1.1 root hub
Bus 001 Device 003: ID 058f:6254 Alcor Micro Corp. USB Hub
Bus 003 Device 002: ID 1631:5400 Good Way Technology 
Bus 003 Device 003: ID 1631:5002 Good Way Technology 
Bus 003 Device 004: ID 045e:00f6 Microsoft Corp. Comfort Optical Mouse 1000
Bus 001 Device 008: ID 0781:5535 SanDisk Corp. 
Bus 002 Device 006: ID 18d1:0003 Google Inc. 

Observo el dispositiu Bus 002 Device 006: ID 18d1:0003 Google Inc. És la meva tablet. M’interessa l’ID 18dl. Aquest paràmetre el faré servir per a crear un fitxer de filtre amb

sudo leafpad /etc/udev/rules.d/51-android.rules

Si el fitxer no existeix, aleshores el crea. Cal afegir la línia següent. En negreta, el ID Vendor que he trobat amb lsusb

SUBSYSTEM=="usb", ATTR{idVendor}=="18d1", MODE="0666", GROUP="plugdev"

i li donem els permisos adequats amb

sudo chmod a+r /etc/udev/rules.d/51-android.rules

Finalment, el fitxer que he creat:

albert@athena:~$ ls -al /etc/udev/rules.d/51-android.rules
-rw-r--r-- 1 root root 70 ago 17 19:25 /etc/udev/rules.d/51-android.rules
albert@athena:~$ 

Amb l’anterior ja hauria de tenir prou com per a poder activar el DDMS i l’ADB

Si tot ha anat bé, Engegant el DDMS (l’script ddms el trobaré a HOME_ANDROID_SDK/platform/tools/) el dispositiu apareixerà “online”, com a la imatge de sota.

En cas de no haver-se pogut establir la connexió, apareixeran ?????????.???. Caldrà revisar què és el que no hem fet bé.

En aquest moment, amb el DDMS ja podem fer coses com:
– còpies de la pantalla del dispositiu Android.
– enviar i rebre fitxers a i del dispositiu.
– analitzar el consum de memòria de les aplicacions del dispositiu
– analitzar el tràfic de xarxa de les aplicacions.
– emular l’entrada i sortida de trucades telefòniques, SMS, simular ubicació per a testejar el posicionament.
– …

Python en Server Mode

A continuació he posat l’SL4A en Server Mode. Primer de tot, obro SL4A

Per comoditat, preestableixo el port del Server Mode a 50000 (ha de ser un port lliure, i els port “alts”, per sobre de 32768 acostumen a estar-ho). Com alternativa, si poso 0 al valor del port, aleshores el SL4A és el que tria el port. En aquest últim cas, però, com em caldrà després, hauré de revisar quin port ha establert.

Per a fer-ho, primer de tot, al SL4A obro el menú de preferències:

Trio l’opció Server Port:

I l’estableixo a 50000.

A continuació, obro la vista d'”Interpreters”

Activo el mode Servidor (Start Server)

I l’inicio en mode “Privat”

En aquest moment, el SL4A – Py4A ja està iniciat en mode servidor, com podem veure a la notificació:

Vull confirmar que el port del servidor és el 50000. Si faig click a l’icona de l’SL4A s’obre la notificació que em mostra els scripts de SL4A que estan en funcionament:

I obtinc el port. Efectivament és el 50000.

Ja gairebé ho tinc tot a punt.

android.py

Finalment, he d’obtenir de la carpeta d’android el fitxer android.py i l’he de copiar a l’ordinador. Aquest android.py s’ha d’importar en els scripts de Py4A i, per tant, haurà de ser “visible” des de l’interpret de python de l’ordinador. Android.py actua com a proxy entre l’interpret de Python i l’SL4A. Aquesta comunicació quan el SL4A està en server mode es fa per RPC, de forma que realment no cal que l’interpret i el SL4A resideixin a la mateixa màquina. Aquest fet és el que permet desenvolupar a l’ordinador i executar a la tablet.

Per a obtenir el fitxer android.py de la tableta faré servir el File Explorer del ddms.

El fitxer es troba a /sdcard/com.googlecode.pythonforandroid/extras/python

Aleshores fent servir el botó “Pull File From Device”, El botó amb l’icona del disquet del File Explorer, puc copiar android.py a la carpeta de fonts python amb la que treballaré. En el meu cas l’he copiat a /home/albert/workspace/wk-python/prova-py4a

adb forwardind
Finalment, obro un terminal i em poso a la carpeta de fonts, i faig el següent:

albert@athena:~/workspace/wk-python/prova-py4a$ /home/albert/android-sdk-linux/platform-tools/adb start-server
albert@athena:~/workspace/wk-python/prova-py4a$ /home/albert/android-sdk-linux/platform-tools/adb forward tcp:9999 tcp:50000
albert@athena:~/workspace/wk-python/prova-py4a$ export AP_PORT=9999

El port 9999 és troba a la major part de la literatura sobre SL4A, però és per convenció.

Fixem-nos que les quatre línies anteriors sempre seran les mateixes, o sigui que les puc posar en un script.

Dit i fet, el meu script per engegar l’entorn d’sl4a és aquest.

#!/bin/bash

/home/albert/android-sdk-linux/tools/ddms &
/home/albert/android-sdk-linux/platform-tools/adb start-server
/home/albert/android-sdk-linux/platform-tools/adb forward tcp:9999 tcp:50000
export AP_PORT=9999
idle &

Li en dono els permisos d’execució (chmod a+x sl4a)

A L’script primer engego el ddms per si em cal passar scripts de l’ordinador a la tablet, o per si cal controlar l’us de memòria, comunicacions, o per si vull fer còpies de pantalla… Després engeo l’adb i li passo els ports 9999, i 50000 (el que he preestablert per al SL4A Server Mode). Finalment, exporto la variable AP_PORT i, finalment, engego IDLE, l’IDE de pyhton construit amb TkInter.

En cas que no el tingueu instal·lat, IDLE es pot instal·lar amb Synaptic, o des del Centre de Programari de l’Ubuntu

Finalment, des de l’IDLE faig la prova canònica:

I, com no podria ser d’una altre forma, funciona 😉

Hem construït un entorn de desenvolupament per a SL4A i Py4A amb:
Un cable USB
Un ordinador amb
Lubuntu 12.01
JDK1.6
Android SDK
IDE de Python IDLE
Una tablet barata.

Albert Baranguer – 26.08.2012

HTML5. WebSockets amb Jetty

Continuant amb l’exploració de l’HTML5, avui parlo dels WebSockets. I per a il·lustrar-lo, faré un petit exercici amb websockets fent servir el servidor java Jetty.

La idea de l’exemple l’he tret del projecte http://code.google.com/p/phpwebsocket/. El client html és pràcticament idèntic. La part del servidor, en el meu exemple, l’he implementada amb Java; estant en PHP en l’original.

Els WebSockets permeten realitzar la comunicació bidireccional i full-duplex sobre un canal TCP, entre la pàgina web i el servidor de forma molt més eficient que amb l’esquema de petició-resposta de l’HTML, o que la comunicació amb AJAX o, més explí­citament, mitjançant Comet.

El protocol WebSocket es descriu a la RFC6455.

A dia d’avui, aquest protocol es troba implementat per les darreres versions dels principals navegadors:  Chrome, Internet Explorer, Firefox i Safari.

A més, el servidor ha de soportar també els WebSockets de servidor.

En si mateix el protocol recorda una crida HTTP, però no ho és:

Exemple de petició des del client:

GET /mychat HTTP/1.1
Host: server.example.com
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Key: x3JJHMbDL1EzLkh9GBhXDw==
Sec-WebSocket-Protocol: chat
Sec-WebSocket-Version: 13
Origin: http://example.com

Exemple de resposta del servidor:

HTTP/1.1 101 Switching Protocols
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Accept: HSmrc0sMlYUkAGmm5OPpG2HaGWk=
Sec-WebSocket-Protocol: chat

Des del punt de vista del desenvolupament parlem, doncs, de clients i servidors de WebSockets.
HTML5 ofereix una API (normalitzada pel W3C) per a desenvolupar clients d’aplicacions basades en WebSocket.

Per la banda del servidor, els llenguatges més utlitzats, com Java o php aporten classes, mètodes i funcions per a crear i utilitzar websockets de servidor.

Sobre servidors Java: jWebSocket, Apache Tomcat i Jetty també tenen implementacions de sockets de servidor.
Sobre servidors PHP: phpWebSockets

Podem trobar implementacions de servidor WebSocket amb altres llenguatges. Per exemple amb Python una cerca ens proporciona: pywebsocket, o WebSocket for Python (ws4py).

Per a fer un experiment senzill utilitzaré el servidor de java servlets Jetty (versió 8) que proporciona una implementació java de WebSockets de servidor. Podeu revisar aquest article: Jetty WebSocket Server, i aquest altre: Jetty/Feature/WebSockets.
Per descomptat, el javadoc: http://download.eclipse.org/jetty/stable-8/apidocs/

Per a provar els WebSockets he creat un petit projecte amb Eclipse, amb la següent estructura de carpetes:

A la carpeta src hi creo el package java amb la següent classe: com.stsoftlliure.proves.html5.websockets.ProvaWebSocket

Fitxer ProvaWebSocket.java:

package com.stsoftlliure.proves.html5.websockets;

import java.io.IOException;
import java.util.Date;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.eclipse.jetty.websocket.WebSocket;
import org.eclipse.jetty.websocket.WebSocketServlet;
import org.eclipse.jetty.util.log.Log;
public class ProvaWebSocket extends WebSocketServlet  {

   private static final long serialVersionUID = 8442112605629206192L;


    protected void doGet(HttpServletRequest request,
                         HttpServletResponse response)
    throws ServletException ,IOException  {
      response.getOutputStream().println("aquesta és la resposta");
      Log.info("[doGet] No fa res");
    }

    public WebSocket doWebSocketConnect(HttpServletRequest request,
                                        String protocol) {
      Log.info("[doWebSocketConnect] entra a doWebSocketConnect");
      return new ProvaServerWebSocket();
    }

    // inner class que implementa la interface WebSocket
    class ProvaServerWebSocket implements WebSocket.OnTextMessage {
      // la connexió
      Connection wsConn;
      Date dataInici;

      public ProvaServerWebSocket() {
         dataInici = new Date();
      }

      @Override
      public void onClose(int arg0, String arg1) {
         // TODO Auto-generated method stub
         Log.info("[onClose] arg0:" + arg0 + "; arg1: " + arg1);
         wsConn.close(arg0, arg1);
      }

      @Override
      public void onOpen(Connection wsConn) {
         this.wsConn = wsConn;
         try {
            Log.info("[onOpen] wsConn:" + wsConn);
            wsConn.sendMessage("Estic connectat!");
         } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
            Log.debug("Error: " + e.toString());
         }
      }

      @Override
      public void onMessage(String sMessage) {
         // TODO Auto-generated method stub
         Log.info("[onMessage] Missatge rebut: " + sMessage);
            // fa el tractament:
         // to do
         // help, hola, nom, edat, data, adeu
 
         try {
            if (sMessage.trim().equalsIgnoreCase("help")) {
               wsConn.sendMessage("ordres acceptades: help, hola, nom, edat, data, adeu");
            }

            if (sMessage.trim().equalsIgnoreCase("hola")) {
               wsConn.sendMessage("Hola! benvingut al servidor WebSocket!");
            }

            if (sMessage.trim().equalsIgnoreCase("nom")) {
               wsConn.sendMessage("Jo em dic ProvaWebSocket.class");
            }

            if (sMessage.trim().equalsIgnoreCase("edat")) {
               wsConn.sendMessage("En funcionament des de: " + dataInici);
            }

            if (sMessage.trim().equalsIgnoreCase("data")) {
               wsConn.sendMessage("ara són les: " + (new Date()));
            }

            if (sMessage.trim().equalsIgnoreCase("adeu")) {
               wsConn.sendMessage("Adéu siau!");
               wsConn.close();
            }
         } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
            Log.debug("Error: " + e.toString());
         }
      }
   }
}

Algunes remarques a la classe anterior:

– La classe és un servlet del tipus nou WebSocketServlet.
– Els WebSocketServlet implementen el doGet. El puc invocar directament i obtinc una resposta HTML. Però la funcionalitat apareix amb el nou mètode doWebSocketConnect. A doWebSocketConnect és on treballa el socket de servidor.
-Quan es rep una nova connexió, el mètode doWebSocketConnect el que fa és instanciar la inner class ProvaServerWebSocket, que implementa la interface WebSocket.OnTextMessage.
– La interface WebSocket té altres subinterfaces, a més de la OnTextMessage, per a tractar altres tipus de connexions. Les subinterfaces de WebSocket són: WebSocket.OnBinaryMessage, WebSocket.OnControl, WebSocket.OnFrame, WebSocket.OnTextMessage.
– en la connexió onOpen, obtinc l’objecte Connection wsConn. Que serà propiament el canal full duplex entre servidor i client.
– El tractament d’un missatge rebut el faig en implementar el mètode public void onMessage(String sMessage). sMessage és el missatge rebut des del client. Les respostes al client s’envien mitjançant l’objecte Connection wsConn obtingut en l’event onOpen. En aquest exemple, simplement analitzo quin missatge rebo i, depenent del missatge, envio una o altre resposta al client.
– A la sortida onClose, tanco la connexió

Senzill i net, oi?

L’altre part és la pàgina del client. Una pàgina HTML5 que fa servir websockets ha d’implementar els mètodes que responen als esdeveniments de connexió, alliberament, recepció i enviament de missatges al servidor. Vet aquí la pàgina HTML que dialoga amb el servidor:

Fitxer prova.html

<html>
<head>
<title>WebSocket</title>

<style>
 html,body{font:normal 0.9em arial,helvetica;}
 #log {width:440px; height:200px; border:1px solid #7F9DB9; overflow:auto;}
 #msg {width:330px;}
</style>

<script>
var socket;

function init(){
  var host = "ws://localhost:8080/websockets2/servlet/WebSocket";
  try{
    socket = new WebSocket(host);
    log('WebSocket - status '+socket.readyState);
    socket.onopen    = function(msg){ log("Welcome - status "+this.readyState); };
    socket.onmessage = function(msg){ log("Received: "+msg.data); };
    socket.onclose   = function(msg){ log("Disconnected - status "+this.readyState); };
  }
  catch(ex){ log(ex); }
  $("msg").focus();
}

function send(){
  var txt,msg;
  txt = $("msg");
  msg = txt.value;
  if(!msg){ alert("El missatge no pot ser nul"); return; }
  txt.value="";
  txt.focus();
  try{ socket.send(msg); log('Enviat: '+msg); } catch(ex){ log(ex); }
}

function quit(){
  log("Adéu!");
  socket.close();
  socket=null;
}

// Utilities
function $(id){ return document.getElementById(id); }
function log(msg){ $("log").innerHTML+="<br>"+msg; }
function onkey(event){ if(event.keyCode==13){ send(); } }
</script>

</head>
<body onload="init()">
 <h3>WebSocket</h3>
 <div id="log"></div>
 <input id="msg" type="textbox" onkeypress="onkey(event)"/>
 <button onclick="send()">Send</button>
 <button onclick="quit()">Quit</button>
 <div>Commands: help, hola, nom, edat, data, adeu</div>
</body>
</html>

Alguns comentaris: com es veu, la pàgina és senzilla, la clau del funcionament està al mètode init

function init(){
  var host = "ws://localhost:8080/websockets2/servlet/WebSocket";
  try{
    socket = new WebSocket(host);
    log('WebSocket - status '+socket.readyState);
    socket.onopen    = function(msg){ log("Welcome - status "+this.readyState); };
    socket.onmessage = function(msg){ log("Received: "+msg.data); };
    socket.onclose   = function(msg){ log("Disconnected - status "+this.readyState); };
  }
  catch(ex){ log(ex); }
  $("msg").focus();
}

El qual crea l’objecte socket, el connecta al host, adonem-nos del protocol “ws”; i enllaça els esdeveniments del socket: onopen, onmessage, onclose amb les funcions que els tracten.

L’enviament de missatges al servidor es realitza amb la funció send, que invoca al mètode send de l’objecte socket.

function send(){
  var txt,msg;
  txt = $("msg");
  msg = txt.value;
  if(!msg){ alert("El missatge no pot ser nul"); return; }
  txt.value="";
  txt.focus();
  try{ socket.send(msg); log('Enviat: '+msg); } catch(ex){ log(ex); }
}

Amb l’anterior ja tinc tot el codi. Poso la pàgina prova.html a la carpeta webcontent del projecte Eclipse. A més, en la carpeta webcontent també hi posaré la carpeta WEB-INF, amb una subcarpeta classes buida, la carpeta lib amb les llibreries que em calen i el web.xml.

En resum, a webcontent hi trobo:

webcontent/
    prova.html
    WEB-INF/
        classes/
        lib/
            jetty-util-8.1.2.v20120308.jar
            jetty-websocket-8.1.2.v20120308.jar
        web.xml

Les llibreries a la carpeta lib corresponen a la versió 8 del Jetty i es poden trobar a la seva carpeta lib. He vist que cal incloure-les al war. Em pensava que es referenciarien automàticament al fer deploy al Jetty, però no ha estat així.

El web.xml és força estàndar:

<?xml version="1.0" encoding="UTF-8"?>
<web-app version="2.5" xmlns="http://java.sun.com/xml/ns/javaee"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://java.sun.com/xml/ns/javaee 
	http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd">
	<display-name>tailor</display-name>
	<servlet>
		<servlet-name>WebSocket</servlet-name>
		<servlet-class>com.stsoftlliure.proves.html5.websockets.ProvaWebSocket</servlet-class>
	</servlet>
	<servlet-mapping>
		<servlet-name>WebSocket</servlet-name>
		<url-pattern>/servlet/*</url-pattern>
	</servlet-mapping>

	<welcome-file-list>
		<welcome-file>prova.html</welcome-file>
	</welcome-file-list>
</web-app>

Si de cas, remarcar el mapeig de /servlet/* a WebSocket. Aquesta és l’explicació de
var host = “ws://localhost:8080/websockets2/servlet/WebSocket”;
a la funció init.

Amb Jetty, a més, m’ha calgut establir el “context.” Al projecte, el fitxer de context l’he guardat a la carpeta homònima. És el següent fitxer.

websockets2.xml:

<?xml version="1.0"  encoding="ISO-8859-1"?>
<!DOCTYPE Configure PUBLIC "-//Jetty//Configure//EN" "http://www.eclipse.org/jetty/configure.dtd">

<Configure class="org.eclipse.jetty.webapp.WebAppContext">
  <Set name="contextPath">/websockets2</Set>
  <Set name="war"><SystemProperty name="jetty.home" default="."/>/webapps/websockets2.war</Set>
</Configure>

Compte amb els salts de línia. El fitxer XML de context potser no queda “bonic” tal com està, però us podeu trobar amb problemes estranys si afegiu salts de línia.

Amb el fitxer anterior li estem dient quin és el war que ha de desplegar i quin nom tindrà el context. Amb aquest fitxer de context acabem de completar l’explicació del

var host = “ws://localhost:8080/websockets2/servlet/WebSocket”;

Ara és evident:

ws:// – Protocol websocket. Amb websockets segurs, serà wss://
localhost:8080 – Host i port del servidor websocket
websockets2 – El context, o l’aplicació web.
servlet/WebSocket – el Servlet servidor de WebSocket

Finalment, per automatitzar el muntatge i ddesplegament al servidor jetty local de l’apliació, faig servir el següent build.xml d’ant:

<project name="websocket" default="deploy" basedir=".">
  <description>
  proves amb websockets 2
  </description>

  <!-- estableix propietats globals -->
  <property name="src" location="src"/>
  <property name="bin" location="bin"/>
  <property name="war" location="war"/>
  <property name="webcontent" location="webcontent" />
  <property name="context" location="context" />
  <property name="build" location="build"/>
  <property name="lib" location="/home/albert/jetty/lib" />
  <property name="deploy" location="/home/albert/jetty/webapps" />
  <property name="deploy-context" location="/home/albert/jetty/contexts" />


  <target name="init">
    <!-- time stap -->
    <tstamp/>
    <!-- Crea el directory bin utilitzat en la compilació -->
    <mkdir dir="${bin}"/>
  </target>


  <target name="compile" depends="init" description="compila els fitxers font" >
    <!-- Compila les fonts java de ${src} en ${build} -->
    <javac srcdir="${src}" destdir="${bin}">
      <classpath>
       <pathelement location="${lib}/jetty-websocket-8.1.2.v20120308.jar"/>
       <pathelement location="${lib}/servlet-api-3.0.jar"/>
       <pathelement location="${lib}/jetty-util-8.1.2.v20120308.jar"/>
      </classpath>
   </javac>
  </target>

  <target name="build" depends="compile" description="genera el build">
    <!-- Crea el directori de distribució -->
    <delete dir="${build}" />
    <mkdir dir="${build}"/>

    <!--  copia webcontent a build -->
    <copy todir="${build}" >
        <fileset dir="${webcontent}" />
    </copy>
    
    <!--  copia bin a classes -->
    <copy todir="${build}/WEB-INF/classes">
        <fileset dir="${bin}" />
    </copy>
  </target>
     
  <target name="war" depends="build" description="genera el war">   
    <delete file="${war}/websockets2.war" />
    <jar jarfile="${war}/websockets2.war" basedir="${build}"> 
      <include name="**/*"/>
    </jar>
  </target>
  	
  <target name="deploy" depends="war" description="deploy del war i del context">
    <delete file="${deploy}/websockets2.war" />
    <delete file="${deploy-context}/websockets2.xml" />
  	<copy file="${war}/websockets2.war" todir="${deploy}" />
  	<copy file="${context}/websockets2.xml" todir="${deploy-context}" /> 
  </target>

</project>

Aleshores, ses d’Eclipse puc executar el build.xml que compila la classe, construeix el war i el copia a la carpeta webapps del meu Jetty local.

Per provar l’aplicació només en cal engegar el Jetty i obrir el navegador amb suport de websockets (en el meu cas, el Chrome) i apuntar-lo a l’adreça http://localhost:8080/websockets2

I el resultat és el següent.

I, efectivament, puc mantindre el diàleg amb el servidor:

Els vídeos de la deSymfony2012

Des de fa uns dies ja estan penjats els vídeos de les conferències de la deSymfony 2012 celebrada a Castelló a mitjans de juny passat.

Recordem que Symfony és un framework MVC (i algunacosa més) per a desenvolupar aplicacions amb php.

La deSymfony és la “La conferencia hispana más importante sobre Symfony y la segunda a nivel mundial.”

Molt remarcable la presència i la presentació de Fabien Potencier, el creador de Symfony i CEO de SensioLabs.

Font: http://desymfony.com/videos. Els enllaços porten a les respectives pàgines de les presentacions a deSymfony.com, on es pot trobar el ppt de la presentació i el vídeo corresponent al YouTube.

Vídeos de la 1ra jornada,  divendres 15 de juny

Vídeos de la 2na jornada,  dissabte 16 de juny