Sep 30

Hello World using ‘kombu’ library and python

Reading time: 4 – 7 minutes

Some times schemas and snippets don’t need large descriptions. If you think this is not enough in this case tell me and I’m going to add explanations.

Using a python library called kombu as an abstraction to talk with AMQP broker we are going to develop different message routes setting each type of Exchange. As a backend I used RabbitMQ with default configuration.

AMQP schema using an exchange of type direct

kombu-direct

Queue definition:

from kombu import Exchange, Queue

task_exchange = Exchange("msgs", type="direct")
queue_msg_1 = Queue("messages_1", task_exchange, routing_key = 'message_1')
queue_msg_2 = Queue("messages_2", task_exchange, routing_key = 'message_2')

The producer:

from __future__ import with_statement
from queues import task_exchange

from kombu.common import maybe_declare
from kombu.pools import producers


if __name__ == "__main__":
    from kombu import BrokerConnection

    connection = BrokerConnection("amqp://guest:guest@localhost:5672//")

    with producers[connection].acquire(block=True) as producer:
        maybe_declare(task_exchange, producer.channel)
        
        payload = {"type": "handshake", "content": "hello #1"}
        producer.publish(payload, exchange = 'msgs', serializer="pickle", routing_key = 'message_1')
        
        payload = {"type": "handshake", "content": "hello #2"}
        producer.publish(payload, exchange = 'msgs', serializer="pickle", routing_key = 'message_2')

One consumer:

from queues import queue_msg_1
from kombu.mixins import ConsumerMixin

class C(ConsumerMixin):
    def __init__(self, connection):
        self.connection = connection
        return
    
    def get_consumers(self, Consumer, channel):
        return [Consumer( queue_msg_1, callbacks = [ self.on_message ])]
    
    def on_message(self, body, message):
        print ("RECEIVED MSG - body: %r" % (body,))
        print ("RECEIVED MSG - message: %r" % (message,))
        message.ack()
        return
    

if __name__ == "__main__":
    from kombu import BrokerConnection
    from kombu.utils.debug import setup_logging
    
    setup_logging(loglevel="DEBUG")

    with BrokerConnection("amqp://guest:guest@localhost:5672//") as connection:
        try:
            C(connection).run()
        except KeyboardInterrupt:
            print("bye bye")


AMQP schema using an exchange of type fanout

kombu-fanout

Queue definition:

from kombu import Exchange, Queue

task_exchange = Exchange("ce", type="fanout")
queue_events_db = Queue("events.db", task_exchange)
queue_events_notify = Queue("events.notify", task_exchange)


The producer:

from __future__ import with_statement
from queues import task_exchange

from kombu.common import maybe_declare
from kombu.pools import producers


if __name__ == "__main__":
    from kombu import BrokerConnection

    connection = BrokerConnection("amqp://guest:guest@localhost:5672//")

    with producers[connection].acquire(block=True) as producer:
        maybe_declare(task_exchange, producer.channel)
        
        payload = {"operation": "create", "content": "the object"}
        producer.publish(payload, exchange = 'ce', serializer="pickle", routing_key = 'user.write')

        payload = {"operation": "update", "content": "updated fields", "id": "id of the object"}
        producer.publish(payload, exchange = 'ce', serializer="pickle", routing_key = 'user.write')

One consumer:

from queues import queue_events_db
from kombu.mixins import ConsumerMixin

class C(ConsumerMixin):
    def __init__(self, connection):
        self.connection = connection
        return
    
    def get_consumers(self, Consumer, channel):
        return [Consumer( queue_events_db, callbacks = [self.on_message])]
    
    def on_message(self, body, message):
        print ("save_db: RECEIVED MSG - body: %r" % (body,))
        print ("save_db: RECEIVED MSG - message: %r" % (message,))
        message.ack()
        return


if __name__ == "__main__":
    from kombu import BrokerConnection
    from kombu.utils.debug import setup_logging
    
    setup_logging(loglevel="DEBUG")

    with BrokerConnection("amqp://guest:guest@localhost:5672//") as connection:
        try:
            C(connection).run()
        except KeyboardInterrupt:
            print("bye bye")


AMQP schema using an exchange of type topic

kombu-topic

Queue definition:

from kombu import Exchange, Queue

task_exchange = Exchange("user", type="topic")
queue_user_write = Queue("user.write", task_exchange, routing_key = 'user.write')
queue_user_read = Queue("user.read", task_exchange, routing_key = 'user.read')
queue_notify = Queue("notify", task_exchange, routing_key = 'user.#')


The producer:

from __future__ import with_statement
from queues import task_exchange

from kombu.common import maybe_declare
from kombu.pools import producers


if __name__ == "__main__":
    from kombu import BrokerConnection

    connection = BrokerConnection("amqp://guest:guest@localhost:5672//")

    with producers[connection].acquire(block=True) as producer:
        maybe_declare(task_exchange, producer.channel)
        
        payload = {"operation": "create", "content": "the object"}
        producer.publish(payload, exchange = 'user', serializer="pickle", routing_key = 'user.write')

        payload = {"operation": "update", "content": "updated fields", "id": "id of the object"}
        producer.publish(payload, exchange = 'user', serializer="pickle", routing_key = 'user.write')

        payload = {"operation": "delete", "id": "id of the object"}
        producer.publish(payload, exchange = 'user', serializer="pickle", routing_key = 'user.write')

        payload = {"operation": "read", "id": "id of the object"}
        producer.publish(payload, exchange = 'user', serializer="pickle", routing_key = 'user.read')

One consumer:

from queues import queue_events_db
from kombu.mixins import ConsumerMixin

class C(ConsumerMixin):
    def __init__(self, connection):
        self.connection = connection
        return
    
    def get_consumers(self, Consumer, channel):
        return [Consumer( queue_events_db, callbacks = [self.on_message])]
    
    def on_message(self, body, message):
        print ("save_db: RECEIVED MSG - body: %r" % (body,))
        print ("save_db: RECEIVED MSG - message: %r" % (message,))
        message.ack()
        return


if __name__ == "__main__":
    from kombu import BrokerConnection
    from kombu.utils.debug import setup_logging
    
    setup_logging(loglevel="DEBUG")

    with BrokerConnection("amqp://guest:guest@localhost:5672//") as connection:
        try:
            C(connection).run()
        except KeyboardInterrupt:
            print("bye bye")


Simple queues

Kombu implements SimpleQueue and SimpleBuffer as simple solution for queues with exchange of type ‘direct’, with the same exchange name, routing key and queue name.

Pusher:

from kombu import BrokerConnection

connection = BrokerConnection("amqp://guest:guest@localhost:5672//")
queue = connection.SimpleQueue("logs")

payload = { "severity":"info", "message":"this is just a log", "ts":"2013/09/30T15:10:23" }
queue.put(payload, serializer='pickle')

queue.close()

Getter:

from kombu import BrokerConnection
from Queue import  Empty

connection = BrokerConnection("amqp://guest:guest@localhost:5672//")

queue = connection.SimpleQueue("logs")

while 1:
    try:
        message = queue.get(block=True, timeout=1)
        print message.payload
        message.ack()
    except Empty:
        pass
    except KeyboardInterrupt:
        break
    print message

queue.close()

The files

Download all example files: kombu-tests.tar.gz

Sep 25

Server send push notifications to client browser without polling

Reading time: 5 – 8 minutes

Nowadays last version of browsers support websockets and it’s a good a idea to use them to connect to server a permanent channel and receive push notifications from server. In this case I’m going to use Mosquitto (MQTT) server behind lighttpd with mod_websocket as notifications server. Mosquitto is a lightweight MQTT server programmed in C and very easy to set up. The best advantage to use MQTT is the possibility to create publish/subscriber queues and it’s very useful when you want to have more than one notification channel. As is usual in pub/sub services we can subscribe the client to a well-defined topic or we can use a pattern to subscribe to more than one topic. If you’re not familiarized with MQTT now it’s the best moment to read a little bit about because that interesting protocol. It’s not the purpose of this post to explain MQTT basics.

A few weeks ago I set up the next architecture just for testing that idea:

mqtt_schema

weboscket gateway to mosquitto mqtt server with javascrit mqtt client

The browser

Now it’s time to explain this proof of concept. HTML page will contain a simple Javascript code which calls mqttws31.js library from Paho. This Javascript code will connect to the server using secure websockets. It doesn’t have any other security measure for a while may be in next posts I’ll explain some interesting ideas to authenticate the websocket. At the end of the post you can download all source code and configuration files. But now it’s time to understand the most important parts of the client code.

client = new Messaging.Client("ns.example.tld", 443, "unique_client_id");
client.onConnectionLost = onConnectionLost;
client.onMessageArrived = onMessageArrived;
client.connect({onSuccess:onConnect, onFailure:onFailure, useSSL:true});

Last part is very simple, the client connects to the server and links some callbacks to defined functions. Pay attention to ‘useSSL’ connect option is used to force SSL connection with the server.

There are two specially interesting functions linked to callbacks, the first one is:

function onConnect() {
  client.subscribe("/news/+/sport", {qos:1,onSuccess:onSubscribe,onFailure:onSubscribeFailure});
}

As you can imagine this callback will be called when the connections is established, when it happens the client subscribes to all channels called ‘/news/+/sports’, for example, ‘/news/europe/sports/’ or ‘/news/usa/sports/’, etc. We can also use, something like ‘/news/#’ and it will say we want to subscribe to all channels which starts with ‘/news/’. If only want to subscribe to one channel put the full name of the channel on that parameter. Next parameter are dictionary with quality of service which is going to use and links two more callbacks.

The second interesting function to understand is:

function onMessageArrived(message) {
  console.log("onMessageArrived:"+message.payloadString);
};

It’s called when new message is received from the server and in this example, the message is printed in console with log method.

The server

I used an Ubuntu 12.04 server with next extra repositories:

# lighttpd + mod_webserver
deb http://ppa.launchpad.net/roger.light/ppa/ubuntu precise main
deb-src http://ppa.launchpad.net/roger.light/ppa/ubuntu precise main

# mosquitto
deb http://ppa.launchpad.net/mosquitto-dev/mosquitto-ppa/ubuntu precise main
deb-src http://ppa.launchpad.net/mosquitto-dev/mosquitto-ppa/ubuntu precise main

With these new repositories you can install required packages:

apt-get install lighttpd lighttpd-mod-websocket mosquitto mosquitto-clients

After installation it’s very easy to run mosquitto in test mode, use a console for that and write the command: mosquitto, we have to see something like this:

# mosquitto
1379873664: mosquitto version 1.2.1 (build date 2013-09-19 22:18:02+0000) starting
1379873664: Using default config.
1379873664: Opening ipv4 listen socket on port 1883.
1379873664: Opening ipv6 listen socket on port 1883.

The configuration file for lighttpd in testing is:

server.modules = (
        "mod_websocket",
)

websocket.server = (
        "/mqtt" => ( 
                "host" => "127.0.0.1",
                "port" => "1883",
                "type" => "bin",
                "subproto" => "mqttv3.1"
        ),
)

server.document-root        = "/var/www"
server.upload-dirs          = ( "/var/cache/lighttpd/uploads" )
server.errorlog             = "/var/log/lighttpd/error.log"
server.pid-file             = "/var/run/lighttpd.pid"
server.username             = "www-data"
server.groupname            = "www-data"
server.port                 = 80

$SERVER["socket"] == ":443" {
    ssl.engine = "enable" 
    ssl.pemfile = "/etc/lighttpd/certs/sample-certificate.pem" 
    server.name = "ns.example.tld"
}

Remember to change ‘ssl.pemfile’ for your real certificate file and ‘server.name’ for your real server name. Then restart the lighttpd and validate SSL configuration using something like:

openssl s_client -host ns.example.tld -port 443

You have to see SSL negotiation and then you can try to send HTTP commands, for example: “GET / HTTP/1.0″ or something like this. Now the server is ready.

The Test

Now you have to load the HTML test page in your browser and validate how the connections is getting the server and then how the mosquitto console says how it receives the connection. Of course, you can modify the Javascript code to print more log information and follow how the client is connected to MQTT server and how it is subscribed to the topic pattern.

If you want to publish something in MQTT server we could use the CLI, with a command mosquitto_pub:

mosquitto_pub -h ns.example.tld -t '/news/europe/sport' -m 'this is the message about european sports'

Take a look in your browser Javascript consle you have to see how the client prints the message on it. If it fails, review the steps and debug each one to solve the problem. If you need help leave me a message. Of course, you can use many different ways to publish messages, for example, you could use python code to publish messages in MQTT server. In the same way you could subscribe not only browsers to topics, for example, you could subscribe a python code:

import mosquitto

def on_connect(mosq, obj, rc):
    print("rc: "+str(rc))

def on_message(mosq, obj, msg):
    print(msg.topic+" "+str(msg.qos)+" "+str(msg.payload))

def on_publish(mosq, obj, mid):
    print("mid: "+str(mid))

def on_subscribe(mosq, obj, mid, granted_qos):
    print("Subscribed: "+str(mid)+" "+str(granted_qos))

def on_log(mosq, obj, level, string):
    print(string)

mqttc = mosquitto.Mosquitto("the_client_id")
mqttc.on_message = on_message
mqttc.on_connect = on_connect
mqttc.on_publish = on_publish
mqttc.on_subscribe = on_subscribe

mqttc.connect("ns.example.tld", 1883, 60)
mqttc.subscribe("/news/+/sport", 0)

rc = 0
while rc == 0:
    rc = mqttc.loop()

Pay attention to server port, it isn’t the ‘https’ port (443/tcp) because now the code is using a real MQTT client. The websocket gateway isn’t needed.

The files

  • mqtt.tar.gz – inside this tar.gz you can find all referenced files
Sep 23

Home heating using Panstamp (Arduino + TI C1101) and SSR

Reading time: 3 – 5 minutes

Last weekend I worked on setting up home heaters using Panstamp. Panstamp is an Arduino board with Texas Instruments radio. Next winter we’re going to control our home heater using connected internet devices like the laptop, tablet o mobile phones. In this post I only want to share some pictures about the process to install the electronics inside the heaters changing the old electronic boards with new custom ones.

The parts:

  • AC/DC transformer, outputs 5V. It’s really cheap, in this case free because I have more than 20 of them from old projects.

acdc5v_

  • A small custom PCB designed and made by Daniel Berenguer, the owner of Panstamp. Thanks again Daniel. I bought the PCBs and parts for around 10€ each one.

IMG_20130923_114830

  • TMP36 temperature sensor. It costs about 1,5€ each one.

IMG_20130923_114814

  • Solid state relay (SSR) bought in Alied Express web site for less than 5€.

ssr

The process:

I used a lot of tools, because DIY aren’t my strong hability.

IMG_20130921_141640

Double-head tape and hot-blue gun are need…

IMG_20130921_133746

because I want to use a cork base under the PSU and PCB

IMG_20130921_133723Parallelization of the last process
IMG_20130921_143116Using a cutter I got the units
IMG_20130921_143434SSR setup
IMG_20130921_164651connecting SSR, PCB and PSU
IMG_20130921_170711assembling everything on heater side panel
IMG_20130921_173937finally, mounting side panel on the heater
IMG_20130921_133547

Next weeks, I’ll come back with this subject to talk about the software part.

Sep 20

How to get MP3 file from a WebM video

Reading time: < 1 minute Another title for this post could be: "Getting audio from video clips". Because you could do it with MP4 (Mpeg4), WebM, Mov, FLV, etc. We are going to use ffmpeg to that:

ffmpeg -i input_file.webm -ab 128k -ar 44100 out_file.mp3

The meaning of the parameters:

  • ab: the audio bitrate in bps
  • ar: the audio sample rate in hz

And if you have a directory with a lot of files to convert you could use:

find . -name "*.webm" -print0 |while read -d $'\0' file; do ffmpeg -i "$file" -ab 128k -ar 44100 -y "${file%.webm}.mp3";done

Pay attention to “find” and “while read” commands combinations because we want to support files with spaces.

I hope this is as useful for you as for me.

Sep 16

RTMP source to HLS (HTTP Live Streaming) Apple

Reading time: 2 – 3 minutes

I just solved a very specific problem and I have to write some notes here to remember the solution. Given a RTMP source we have to stream the content to Apple devices like iPad, iPhone and iPod because RTMP couldn’t be played using Safari browser.

If we need to play streaming on Apple devices the best solution is convert it to HLS and publish generated files using HTTP server.

To solve this issue I use rtmpdump and vlc. Firstly rtmpdump gets video stream from source. Secondly the stream is sent to vlc and finally vlc transcodes de video and audio and outputs small .ts files and one .m3u8 index file.

The command is something like this:

rtmpdump -v -r "$RTMP" | sudo -u xymon vlc -I dummy fd://0 vlc://quit --sout="#transcode{width=320,height=240,fps=25,vcodec=h264,vb=256,venc=x264{aud,profile=baseline,level=30,keyint=30,ref=1,nocabac},acodec=mp3,ab=96,audio-sync,deinterlace,channels=2,samplerate=44100}:std{access=livehttp{seglen=10,delsegs=true,numsegs=5,index=$M3U8,index-url=$TS_URL},mux=ts{use-key-frames},dst=$TSF}"

Variables descriptions are:

RTMP=rtmp://example.tld/path/stream_id
WD=/local_path
TS=live-####.ts
TSF=$WD/$TS
TS_URL=http://example.tld/path/$TS
M3U8=$WD/live.m3u8

Then create an HTML file, for example live.html, with a reference to .m3u8 file, the relevant code of the HTML file is like this:

<video width="320" height="240"><source src="http://example.tld/path/live.m3u8" /></video>

A simple code to public files via HTTP:

python -c "import SimpleHTTPServer;SimpleHTTPServer.test()"

Then we only need to open Safary browser in Apple device and set the proper URL, in our case:

http://example.tld/path/live.html

IMPORTANT NOTE: the audio output have to be with two channels and a sample rate of 44KHz in other cases the audio fails.

Sep 06

Celery logs through syslog

Reading time: 2 – 2 minutes

Celery logs are colorized by default, the first big idea is disable color logs. It’s as easy as setting ‘CELERYD_LOG_COLOR’ to ‘False’ in ‘celery.conf’. The code could be something like this:

celery.conf.update('CELERYD_LOG_COLOR' = False)

Secondly we need a function where we set up a new handler and other settings to celery logging system. For example, the code could be:

from __future__ import absolute_import
from logging import BASIC_FORMAT, Formatter
from logging.handlers import SysLogHandler
from celery.log import redirect_stdouts_to_logger

def setup_log(**args):
    # redirect stdout and stderr to logger
    redirect_stdouts_to_logger(args['logger'])
    # logs to local syslog
    hl = SysLogHandler('/dev/log')
    # setting log level
    hl.setLevel(args['loglevel'])
    # setting log format
    formatter = Formatter(BASIC_FORMAT)
    hl.setFormatter(formatter)
    # add new handler to logger
    args['logger'].addHandler(hl)

Pay attention to ‘redirect_stdouts_to_logger’ it’s used to send all outputs like print’s or something else to syslog.

Thirdly we want to use those settings in our celery tasks, then we have to connect ‘setup_log’ code to some celery signals. Those signals are launched when ‘task_logger’ and ‘logger’ are configured. To connect signals:

from celery.signals import after_setup_task_logger, after_setup_logger

after_setup_logger.connect(setup_log)
after_setup_task_logger.connect(setup_log)

Fourthly we have to get the ‘logger’, we can have more than one if we are interested in records with task context or without it. For example:

logger = get_logger('just_a_name_for_internal_use')
logger_with_task_context = get_task_logger('name_of_the_task_to_be_recorded_in_logs')

Finally we only have to use those loggers with common methods DEBUG, INFO, WARN, ERROR and CRITICAL:

@celery.task
def the_task():
    logger.info('this is a message without task context')
    logger_with_task_context.debug('this record will have the prefix "name_of_the_task_to_be_recorded_in_logs" in syslog')
Aug 28

Technitium MAC Address Changer

Reading time: 1 – 2 minutes

I just want to share with you a small and powerful Windows tool I found in my last trip to US. The best feature IMHO is that permits to change the MAC address of your NIC interface without rebooting, safely and fast. It could be useful when you have a limit time to connect to internet in a free Wi-Fi network; after changing your MAC address you should be like a new device. If you have to do something like this, remember to remove the browser cookies.

Other interesting features of this tool is network presets. You can change your NIC settings very fast just changing a preset profile. As you can see in next screenshot it has a simple chart of your real time network traffic. And finally I want to stand out you can see all your network devices configuration very fast.

technitium MAC address changer screenshot

Technitium MAC Address Changer home page.

Aug 17

Route 66 – dia #19 – de San Francisco, California a Barcelona

Reading time: 4 – 6 minutes

El viatge de tornada ha estat via Frankfurt, com sempre passa en aquests casos tot plegat s’acaba allargant unes 24h. Així ara el que toca és viure amb el jet lag durants uns dies. El que volia fer en aquest article és aprofitar per comentar temes relacionats amb el viatge però que per un motiu o altre no he pogut afegir en el dia a dia.

Del primer que vull parlar és de la gent, nosaltres anavem amb el xip de que ens trobariem amb unes persones més aviat tancades, pre-potents i no massa amables. La nostre sorpresa ha estat trobarnos en just el contrari, en molts llocs incloses grans ciutats la gent et saluda fins hi tot sense venir a “cuento” de res, sempre estan apunt per fer-te un comentari amable. Fins hi tot aguantar-te la porta al entrar o sortir dels llocs.

A l’hora de conduir són molt més pacients que en el nostre país, rarament et piten i s’esperen el que calgui per facilitar-te la conducció. Com a tot arreu ens hem trobat excepcions i generalitzar sempre és dolent, tot i que en el nostre cas he de dir que els pocs casos de persones fora del patró que descric han estat hispanes i dels 9 estats que hem visitat el que la gent s’ha mostrat menys amigable ha estat California.

Quan vas a l’estranger sempre troves senyals de transit o noms de negocis que són graciosos. En aquest viatge això era una mica més difícil perquè malgrat no hagis anat mai als EUA tots consumim moltíssimes produccions audiovisuals fetes en aquest país i llavors tot plegat ja és més familiar. Així que si m’he de quedar amb un nom gracios és el d’una carretera que es deia: Zzyzx a California.

IMG_6603

Una altre cosa curiosa o que em va sorprendre moltíssim és la quantitat de gent que sap castellà, sobretot a California. Ja no només les persones d’origen hispà, com podia passar a Nou Mèxic, sinó persones autòctones que com a mínim xapurrejaven el castellà.

Quan vaig estar a Panamà vaig aprendre que quan un semafor esta vermell es pot girar a la dreta, cosa que hem costava posar a la pràctica però que a base de tocs de pito dels que tenia darrera vaig aprendre. Doncs bé, la qüestió és que això és així perquè als EUA és una pràctica permesa i súper habitual. Això si, rarament et pitarà algu si estas en un carril de només girar a la dreta en un semafor vermell i no intentes passar.

Com es demana el menjar de forma habitual també és força curiós, en general no és gens car menjar, obviament si vols gastar diners ho pots fer. Tot i que amb uns 10$ pots menjar moltíssim. La idea és que tu demanes normalment un plat que costa aproximadament aquesta quantitat i això acostuma a incloure un o dos “sides”, que sovint pots escollir entre patetes fregides, patates al forn, una sopa, una amanida, una amanida de col o alguna coseta així. El restaurant de diari que més em va agradar és la cadena Denny’s; amb beguda “re-fill” com és típic i amb mil combinats dels que a mi m’agraden. Com a gran descobriment la “Pink Lemonade”, una llimonada rosa que esta molt bona.

Hi ha una pregunta típica d’aquest viatge: “què ús ha agradat més?” doncs la veritat no és senzill contestar-ho perquè hem vist moltíssimes coses. El “Painted Desert” és una passada, però clar després penses amb el “Grand Canyon” i que t’haig de dir. Arribats aquí canvio d’estartegia anem a pensar en ciutats, Chicago molt xula i verda, però al costat de San Francisco la veritat no hi ha color. Quan ja no saps per on tirar penses, en els poblets petits com Williams (Arizona) o fins hi tot amb Santa Fe (Nou Mèxic) que malgrat ser la capital de l’estat és un poble. Però posar això per davant de la costa: Venice, Santa Monica, o llocs més entrenyables com Big Sur, Morro Bay o Pacific Grove a Monterey doncs la veritat la cosa torna a ser impossible de comparar. Dit d’altre forma, almenys jo sóc no puc dir en quedar-me. Per tant només puc dir que és una experiència que s’ha de viure i que vull recomanar a tothom.

Aug 15

Route 66 – dia #18 – Silicon Valley, California

Reading time: 6 – 10 minutes

Aquest matí hem intentat tornar a passar pel Presidio Park i la Baker Beach; però la boira de San Francisco ens ha continuat impedint treure una bona foto del Golden Gate. El millor que hem pogut fer ha estat això.

IMAG3267

Molt aprop hi ha el Golden Gate Park, un parc senzillament inmens. Hi ha diverses carreteres que el recorren per dintre. El parc és molt maco, digne de passar-hi un dia sencer o més. Amb l’agenda que portem nosaltres ens hem conformat en fer un circuit per dintre del parc.

IMAG3281 IMAG3283 IMAG3290

Al sortir del parc ens ho hem fet venir bé per anar a visitar una de les zones que més li agraden a l’Ernest. És una zona amb un ambient molt hippie, amb diverses botigues de segona mà i un ambient molt jove. Concretament la zona esta a les cantonades de Haight-Ashbury. Allà hem aprofitat per fer un branch, per dir-li d’alguna manera. La qüestió és que hem carregat piles omplint-nos la panxeta.

IMAG3301 IMAG3299

Perquè la següent parada ja ha estat a Apple on amb el trànsit de la sortida de la ciutat hem trigat més d’1h per arribar fins a Cupertino (CA). De les oficines de l’empresa de la poma només hem pogut visitar la botiga orientada als visitants, amb uns preus força cars. Almenys tenien Wi-Fi gratuït i Estefania ha aprofitat per parlar amb la família per Skype.

IMAG3306 IMAG3307

Una de les parades que més m’ha agradat del dia ha estat la del “Computer History Museum“, una passada. He disfrutat com un nen petit. Per cert, el museu esta físicament on abans hi havia Silicon Graphics, una companyia que potser molta gent no recorda però jo em vaig passar molt temps somiant en comprar-me un O2 quan era un adolescent. Fins hi tot tinc una fotografia on estic interactuant amb el seu Irix quan no deuria tenir més de 15 o 16 anys.

Allà he pogut veure el Cray-1, un dels primers súpercomputadors que es va fer l’any 1976.

IMG_20130815_150935

El primer ordinador que vaig tocar a la meva vida, un Compact Macintosh per programar en Logo. ( és el del mig )

IMG_20130815_154754

El primer PC que vaig fer anar.

IMG_20130815_154434

El que es considera el primer PC de la història, el Kenbak-1.

IMG_20130815_154210

Primer rack de servidors de Google.

IMG_20130815_155202

Primers models de ratolins.

IMG_20130815_153950

El primer disc dur.

IMG_20130815_150128

i moltíssimes més coses que millor no poso perquè sinó hi haurà molta gent que s’acabarà adormint.

A menys de dues milles hi ha “Googleplex“. Les famosíssimes oficines de Google, on segons diuen és el millor lloc on es pot treballar avui en dia. Lloc que ens hem quedat amb les ganes de visitar per dintre i només hem pogut fer-nos les fotografies de rigor amb els androids.

IMG_20130815_164556 IMG_20130815_170553

Sortint de Google voliem visitar Facebook, però la direcció que havia aconseguit per internet ens ha portat a un complex d’oficines envoltat pel carrer “Hacker way” on no hem pogut veure cap referència de Facebook per enlloc.

Així que finalment ens hem dirigit cap a l’hotel que avui és el “Best Western Plus Coyote Point Inn” a uns 10 minuts de l’aeroport de San Francisco on demà al matí agafem el vol de tornada cap a casa. Comentar també que pel camí hem vist: Evernote, Checkpoint, Salesforce, SAP, Oracle, Microsoft i alguna altre cosa que no em ve al cap. Pel que fa a universitats de passada hem vist: Standford, Berkeley i Carnegie Mellon.

Han estat 18 dies molt intensos i amb molts quilòmetres recorreguts, però té un final i aquest és el d’aquest viatge. Possiblement escriure un article més quan hagi pogut absorvir millor tot el que hem fet i comentaré alguna de les coses que més m’han xocat en el viatge. Però el diari de viatge acaba aquí. Desitgem que ús hagi agradat seguir-nos.

Aug 14

Route 66 – dia #17 – de Pacific Grove, California a San Francisco, California

Reading time: 4 – 7 minutes

Finalment avui és l’únic dia que haviem dedicit dedicar a San Francisco (CA). Per això hem matinat i fiant-nos del que deia el GPS esperavem arribar a aquesta ciutat abans de les 11 del matí. Però resulta que el trànsit ens ha estat retenint i hem acabat arribant a les 12h passades. L’anècdota ha arribat en un moment que haviem d’anar als “Restrooms” i al parar per casualitat hem passat per les instal·lacions de Google i Youtube, cosa que teniem programada per demà. Així doncs, hem passat quasi sense mirar. Aprofitant la parada tècnica també hem passat per un Best Buy que hi havia a l’altre costat de l’autovia i hem comprat un Nexus 7; el model que fins el mes que ve no surt al nostre país.

Així doncs, amb el retard acumulat hem hagut de fer algunes retallades al que teniem previst per avui i posar una miqueta el turbo. El primer que hem anat a visitar han estat les vistes des de Twin Peaks, aprofitant que no hi havia gens de boira.

IMAG3195 IMAG3190

Seguidament ens hem acostat a Alamo Square, a veure les típiques cases victorianes. Que han aguantat el gran terretremol de la ciutat i el posterior incendi.

IMAG3196

Hem dinat a l’Embarcadero, concretament al “Pear 1″.

IMAG3205 IMAG3204

Següent parada al Coit Tower, on també hi ha unes molt bones vistes. Sobretot de la badia i el moll.

IMAG3218 IMAG3211

Com alguns ja sabeu una de les meves debilitats és la xocolata i l’Ernest em va comentar que no podia deixar de passar-me per la botiga que Ghirardelli té a la zona del Fisherman’s Wharf; una de les zones més turístiques i típiques de la ciutat, on fins hi tot hi ha una petita platja, a més de mil i una activitats pensades pels turístes. Nosaltres ens hem centrat en la xocolata i en fer quatre fotos.

IMAG3232 IMAG3229

Abans d’anar cap a l’hotel hem passat per la típica baixada de Lombard St. Hem fet fotos des de baix i fins hi tot ens hem permès baixar-la amb el nostre cotxe.

IMAG3235

L’hotel on avui dormim és el Marina Motel que esta al mateix carrer Lombard St però a la zona del Marina district, a pocs carrers del Golden Gate Park. L’hotel és molt cuco i sembla tret d’un compte. Això si, diria que ens costarà dormir perquè hi ha molt soroll de cotxes.

IMAG3246 IMAG3243

Després de deixar les coses a l’hotel ens hem escapat a veure el Golden Gate Park; bé de fet, el que voliem era veure la posta de sol des de la platja de Baker Beach, com ens havien recomanat però al arribar hi havia tanta boira que no es veia a més d’uns pocs metres. Així que hem anat a l’altre costat del Park, el que dona a la badia pel costat del Marina district i allà malgrat hi havia boira es podia veure fins hi tot una part important del Golden Gate.

Quan el sol s’ha amagat, ens hem escapat a sopar al nostre barri en un restaurant molt mediterrani. Només diré que ens hem menjat una sopeta i pop a la planxa; boníssim.