Samba server and MacOS client

In order to setup a samba server, you can follow the RaspberryPi guide for it.

Make sure to add permissions to the shared folder on the server, so that the SMB daemon will have permissions to write on it.

In addition, I found that when mounting the share on a MacOS computer, there were weird errors about files being in use and the like. The problem is that MacOS Finder adds a special .DS_Store file to every folder and it produced a conflict when updating the contents of the file.

In order to solve it, just add this to your share, editing the smb.conf file (usually located on /etc/samba or the like):

veto files = /._*/.DS_Store/.Trashes/.TemporaryItems/

delete veto files = yes

The second line allows vetoed files to be deleted. Otherwise, already existing vetoed files would be stuck on the drive. (Note that “._*” prevents HFS+/APFS extended attributes from being stored on the SMB drive.)

From now on, .DS_Store and other conflicting files will not be created on the server.

Rclone for Dropbox on RaspberryPi (and other headless server)

Dropbox does not maintain their client for RaspberryPi, so you should find other ways to use it. There are some non-official clients, but I prefer using rclone, since it allows some interesting features for me:

  • Mount the remote as a local filesystem. This way I can expose a single Dropbox account to multiple users, for example, using the mounted Dropbox folder as an external storage in a Nextcloud server.
  • Sync files from and to Dropbox with different parameters, like limited bandwidth, parallel requests, etc.
  • It allows configuring the number of concurrent request to the remote, so you can speed up or reduce it accordingly to your requirements. In the case of Dropbox, a limit of 8-10 concurrent requests is enough to keep down the limit.

There is a guide on the rclone web site on how to configure a Dropbox remotely, by creating a new Dropbox App. Follow the guidelines there but do not complete the authorization stage.

For a headless server the process is a little bit more tricky and requires you create a remote SSH tunnel to redirect a local port to your desktop running the ssh client. This way you will be able to run the OAuth2 workflow and authorize your remote headless server to access your Dropbox account.

In your desktop (or client) computer, run:

ssh -L localhost:53682:localhost:53682

It will then open a session to your remote server that will tunnel requests on port 53682 to your desktop computer on port 53682.

Then run the following command on the headless remote server:

rclone config reconnect remote-name:

Make sure you include the semicolon at the end, or rclone will complain. Here it is a sample of the output:

Already have a token - refresh?
y) Yes (default)
n) No
y/n> y

Use web browser to automatically authenticate rclone with remote?
 * Say Y if the machine running rclone has a web browser you can use
 * Say N if running rclone on a (remote) machine without web browser access
If not sure try Y. If Y failed, try N.

y) Yes (default)
n) No
y/n> y

2024/04/13 16:45:53 NOTICE: Make sure your Redirect URL is set to "http://localhost:53682/" in your custom config.
2024/04/13 16:45:53 NOTICE: If your browser doesn't open automatically go to the following link:
2024/04/13 16:45:53 NOTICE: Log in and authorize rclone for access
2024/04/13 16:45:53 NOTICE: Waiting for code...

The command does not finish and it is waiting for you to complete the authorization flow on your desktop computer.

Now copy the URL that shows in the output and paste it in to a browser on your desktop computer:

It will redirect you to a Dropbox Oauth screen. Log in, and then review the notice that shows about the authorization you are giving.

Once you complete the authorization, the access code is automatically fed into rclone configuration. If you have a look at the command on your remote server, you will see the rclone command has completed with a message like:

2024/04/13 16:45:53 NOTICE: Make sure your Redirect URL is set to "http://localhost:53682/" in your custom config.
2024/04/13 16:45:53 NOTICE: If your browser doesn't open automatically go to the following link:
2024/04/13 16:45:53 NOTICE: Log in and authorize rclone for access
2024/04/13 16:45:53 NOTICE: Waiting for code...
2024/04/13 16:46:13 NOTICE: Got code

In case you are getting a HTTP 400 error, like “Invalid redirect_uri. It must exactly match one of the redirect URIs you’ve pre-configured for your app (including the path).”, review the Dropbox App description on your Dropbox developer console, and make sure there is an ending ‘/’ on the redirect URL settings, so it is like the one described in the above blog: “http://localhost:53682/”

Developing for Kstars

To build a stable or latest version of kstars, I recommend using the script from

The script creates two folder trees: one for the source code and another one for the build folders, that will include all the compiled objects and artifacts.

Once the script has created the build folder structure, you can manually build other parts of the application, like custom drivers or a new feature you would like to contribute.

I use the script to bootstrap the development and thus having a working copy of the app.

You can modify the original scripts and use your own repository or branch. Copy the original scripts and fine tune them for your repository and branch.

If you modify a driver, make sure to also edit file $INDIROOT/drivers.xml to sync the version.

Use make help to get a list of all available targets

You can also manually rebuild parts of the components. Here there are some tips.

Create build folder structure

You first need to create the building folder structure. This is the way the scripts create the build structure, so you can also make your own customization.

$ [ ! -d ../build-indi ] && \
 { cmake -B ../build-indi ../indi -DCMAKE_INSTALL_PREFIX=/usr -DCMAKE_BUILD_TYPE=Release || \
{ echo "INDI configuration failed"; exit 1; } }
$ cd ../build-indi

Workflow for testing a new version of a driver

In order to run your own version of a driver, you need to first build it. Here are the steps I follow:

  • Make your changes on the corresponding code.
  • Build the driver from the build folder.
  • Copy the driver to the driver install folder.

Use make help to get a list of all available targets in case of doubt.

For example, to build SnapCap driver:

$ cd ~/astro-soft-stable/build-indi/drivers/auxiliary

$ make indi_snapcap
[  0%] Built target hid
[  9%] Built target indicore
[ 22%] Built target indidevice
[ 22%] Built target eventloop
[ 31%] Built target dsp
[ 36%] Built target fpack
[ 95%] Built target indidriver_OBJECT
[ 95%] Built target indidriver
[100%] Built target indi_snapcap

To build a single driver:

  1. go to the build-* driver folder
  2. run make
  3. to install run sudo make install

For example, to build Rolloffino driver on the indi-3rdparties repository:

$ cd ~/astro-soft-stable/build-indi-3rdparty/indi-rolloffino 
$ make
Scanning dependencies of target indi_rolloffino
Building CXX object indi-rolloffino/CMakeFiles/indi_rolloffino.dir/rolloffino.cpp.o
Linking CXX executable indi_rolloffino
Built target indi_rolloffino

To install it you can use sudo make install or manually copy the driver to the driver folder (at /usr/bin for a RaspberryPi).

Start indiserver

It is better to manually start indiserver so you can see logs and other debugging information. For example, this will start a server listing on port 7624 and starting two drivers: Snapcap and Simultar filter wheel.

/usr/bin/indiserver -v -p 7624 -m 2048 -r 0 indi_snapcap  indi_simulator_wheel

Once you manually started the server, you can run kstars and create a profile to connect to the indiserver, in this case you should consider it a remote server running on your local computer. The profile should include the devices you used to manually start indiserver, and it is mandatory to include a CCD. In this example, the devices are Filter Simulator, SnapCap and the mandatory CCD, in this case, the Simulator.

Ejemplo de servidor TCP ESP8266

Hay montones de ejemplos de servidor TCP utilizando ESP8266WiFi. Están pensados para hacer peticiones sin estado, al estilo del protocolo HTTP. En este tipo de peticiones, el cliente abre la conexión, envía la petición, recibe la respuesta y luego cierra la conexión, por eso se dice que no tienen estado. Aqui hay un ejemplo de este código, para un servidor HTTP, que es un servidor sin estado (stateless).

El modelo de programación consiste en realizar la gestión de la transacción en el bloque loop del programa. En dicho bucle, se crea y se destruye el objeto cliente. Eso supone que el cliente tiene que volver a abrir la conexión en la siguiente conexión.

Este modelo no es adecuado para clientes que abren la conexión una vez y esperan que el servidor no la cierre. Un ejemplo de este tipo de conexiones es el de los drivers de indilib.

Si necesitas crear un servidor TCP que no cierre la conexión, es conveniente organizar el codigo de otra forma.

Aqui comparto una solución de ejemplo de código.


    Created on: 4.11.2023


#include <ESP8266WiFi.h>

#define MAX_BAUD_RATE 115200
String strData = "";             // a string to hold incoming data

uint16_t port = 8888;            // Port number
WiFiServer server(port);

// WiFi parameters
const char* ssid = "SSID_ROUTER";
const char* password = "password";
WiFiClient client;

void setup() {
  while (Serial.available() > 0)

  WiFi.disconnect();  //Prevent connecting to wifi based on previous configuration

  WiFi.mode(WIFI_STA);  // WiFi mode station (connect to wifi router only)

  // Connect to WiFi
  WiFi.begin(ssid, password);
  while (WiFi.status() != WL_CONNECTED) {
  Serial.println("\nWiFi connected");
  Serial.print("START MIRRORING SERIAL: ");

  // Start server

void loop() {
  // Serial.println("looping..."); // DEBUG
  if (!client){
    // Only request a client in case there is no valid one.
    // The client should persist on the loop so it is a global variable.
    client = server.available();

  if (client) {
    // Serial.println("client...");
    while (client.available() > 0) {
      char c;
      // Serial.println("data is available...");
      // read data from the connected client
      c =;
      strData += c;

    // Serial.println("\nNo more data available");
    if( strData.length()>0){
      Serial.printf("Message received is %s\n", strData.c_str());
      client.write("Response received");
      strData = "";

La clave está en el código marcado:

if (!client){
    // Only request a client in case there is no valid one.
    // The client should persist on the loop so it is a global variable.
    client = server.available();

En este caso, solamente debemos obtener un objeto WiFiClient si no tenemos uno disponible anteriormente. Se trata de obtener uno cuando sea necesario o cuando no esté disponible el anterior. Además, debemos asegurarnos de no llamar a client.stop() en el propio bucle, porque si no, cerraríamos la conexión y el cliente tendría que volver a abrirla.

Este patron de desarrollo se basa en la sobrecarga del operador bool en la clase WiFiClient.

Para verificar que esta solucion es robusta, puedes cargar el sketch anterior en un ESP8266, y por otro lado, desde un PC conectado a la misma WiFi, puedes construir un programa en Python para hacer de cliente. El programa Python abre la conexión con el servidor una única vez y envia continuamente peticiones.

De esta forma hemos construido un servidor TCP persistente, utilizando las librerías básicas de ESP8266. También puedes mejorar el codigo utilizando una librería asíncrona como ESPAsyncTCP.

Wifi para AZGTi

La montura AZ-GTi de Skywatcher se puede conectar de dos formas diferentes:

  • Wi-Fi. Se puede configurar la AZ-GTi para que se conecte con un Punto de Acceso Wi-Fi externo o bien conectarse directamente al PA propio de la montura. Yo prefiero la primera solución, conectando la montura al PA de astroberry. Si quieres más detalles sobre la configuración de, echa un vistazo a este artículo.
  • Cable EQMOD. Se conecta mediante un cable a un ordenador o una RaspBerry Pi. No he probado este tipo de conexión, aunque parece más robusta, no hay problema de que se desconecte la Wifi, aunque te añade un cable más.

SynScan y SynScan Pro

Hay dos versiones del programa, la que yo utilizo es la SynScan Pro, que permite la gestión de monturas ecuatoriales. Hay que tener en cuenta que el firmware de la montura debe ser el que permita utilizarla en modo ecuatorial. Si necesitas actualizar el firmware desde un Mac, echa un vistazo a este artículo.

A partir de este momento, se utilizará el término SynScan aunque se estará haciendo mención a la versión Pro.

Configuración de Wi-Fi de AZ-GTi

Los dispositivos con Wifi se pueden configurar de dos formas: modo Station o modo Access Point (AP). Algunos dispositivos Wifi sólo permiten uno de los modos, y otros permiten los dos modos simultáneamente. El modo Station permite que se conecte como cliente a un AP y el modo AP permite que el dispositivo reciba conexiones de otros dispositivos.

Hay que configurar la montura AZ-GTi para que tenga activada la conexión por Wifi, tanto como AP, para poder acceder a ella en caso de problemas, como en modo Station, para que se conecte por Wifi al HotSpot de Astroberry.

En la configuracion Access Point, la red que la montura utiliza es 192.168.4.X, y la montura actúa como el router en la dirección IP, y asigna direcciones IP consecutivas, por lo que lo más habitual es que el primer dispositivo que se conecte a ella, sea el

En la configuracion Station, la montura se conecta como un cliente a una red Wi-Fi. En este caso se recomienda, que la montura tenga desactivado el DHCP y asignarle una IP fija, con el fin de saber siempre qué dirección utilizar en la conexión.

Entonces para manejar la montura con la app de SynScan hay dos opciones.

La primera es conectar un dispositivo Android directamente a la montura, conectando por Wifi al Access Point de la montura. Normalmente, el SSID de la montura se llama SynScan_XXXXX, donde XXXXX son letras y números en minúsculas, cada montura genera un número diferente y es posible que si actualizas el firmware o reseteas la configuración de la montura este número cambie también, aunque en las ocasiones que he actualizado el firmware (la ultima version que tengo es la v3.40), el SSID se ha mantenido.

La otra opción es pedirle a la montura que se conecte a un AP y conectar nuestro móvil a dicho AP. Así ambos dispositivos estarán en la misma red. Además, podemos tener otros dispositivos conectados a la misma red y acceder con ellos a la montura, por ejemplo, con una RaspberryPi, para controlar la montura. En este caso, la montura AZ-GTi solamente conecta con AP que tengan una clave configurada, así que el hotspot tiene que tener una clave.

Configuración simultánea como AP y Station

Para configurar que la montura ofrezca las dos posibilidades, primero hay que asegurarse de poder conectar con un dispositivo al AP de la montura, y abrir el programa SynScan:

  • ir a Settings > SynScan Wi-Fi,
  • seleccionar el modo Modify Station,
  • activar Station Mode,
  • rellenar el SSID y la clave,
  • desactivar Use DHCP y
  • poner la dirección IP en la casilla de Fixed IP ( Esta dirección tiene que estar en el rango de direcciones que el AP asigna a los dispositivos que se conectan, y sobre todo, debe ser una dirección IP que ningún otro dispositivo pueda utilizar. Si dos dispositivos conectados al AP tienen la misma IP, el AP no sabe a cual de los dos enviar los datos y el dispositivo puede funcionar mal. En mi caso, la intención es conectarlo al AP de una astroberry, y por defecto la red del AP de astroberry asigna direcciones en el rango a la Utilizando la dirección .33 me aseguro de que no haya conflictos. Otros AP pueden utilizar rangos de direcciones diferentes, por lo que se tendrán que adaptar estas intrucciones a esa configuración.

Con esta configuración, para poder conectar desde un dispositivo Android a la montura, primero hay que conectar el dispositivo Android al mismo AP que hemos utilizado en la configuración. A continuación, hay que abrir SynScan en el móvil, en este caso, hay que configurar la aplicación para que se conecte a la dirección IP fija que hemos indicado antes, la Para ello desde SynScan:

  • ir a Settings > Connect Settings,
  • desactivar Find Devices y
  • poner la dirección IP en la casilla de Fixed IP (

Baterías de más de 5v

Es habitual que cuando se practica la astrofotografía se encuentre uno en un lugar remoto donde no hay un enchufe. El equipo necesario suele incluir varias cámaras, un ordenador, la montura, etc. La solución más habitual es utilizar una batería de coche, puesto que muchos de los dispositivos funcionan a 12V.

En mi caso, tengo un equipo de iniciación y “solamente” tengo que conectar:

  • Raspberry Pi, lo utilizo como ordenador para la gestión de las capturas. Tiene un consumo de 5V, con un máximo de 3A, aunque ronda los 2.4-2.5A.
  • Cámara, es una cámara sencilla, ZWO ASI-120M mini, que consume 5V-1A. En realidad, se conecta a la Rasberry Pi, por lo que no es necesario alimentarla directamente.
  • Montura ecuatorial. Es una montura sencilla, Skywatcher AZ-GTi, con un consumo de 12V y un máximo de 1A.

En este artículo explico cómo he resuelto conectar estos dispositivos utilizando una batería externa (powerbank), mediante un adaptador Quick Charge (QC v2.0, v3.0) y/o USB Power Delivery (PD).

Un par de consideraciones:

  1. La batería externa debe ser compatible con protocolos QC o PD, de forma que sea capaz de configurarse para diferentes voltajes.
  2. El adaptador NO realiza una transformación de voltaje. El adaptador simula ser un dispositivo QC o PD, y en función de cómo lo configuremos, envía una señal a través del bus USB para que la batería externa se configure con diferentes voltajes de salida, la transformación la realiza la propia batería. Por tanto, si tu batería externa no es compatible con QC o PD, esta solución NO te va a funcionar. Los detalles del funcionamiento de dichos protocolos los puedes encontrar en Wikipedia.
  3. Existen en el mercado transformadores de voltaje que permiten pasar de 5V a 12V, pero esta conversión consume energía.

Consumo de cada dispositivo

La Rpi tiene una especificacion de consumo de 5V-3A, máximo. He comprobado que su consumo está en máximos de 1.2A, con la cámara conectada. Puedo conectarla a cualquiera de los puertos USB de una batería, siempre que el puerto pueda entregar hasta 3A, que es el consumo máximo que demanda la Rpi.

El motor de la montura AZ-GTi tiene un consumo máximo de 12V-0.75A durante el movimiento a máxima velocidad, moviendo a la vez los dos ejes.

Las baterías disponen de salidas USB compatibles que en su configuracion por defecto, solamente entregan 5V de salida y hasta 3A. Para otras configuraciones de voltaje diferentes, los puertos USB con Quick Charge (QC 2.0 y 3.0) y con USB Power Delivery (PD) permiten llegar a los 12V que necesita la montura, pero para ello se necesita un dispositivo compatible con esos protocolos.

Si solamente tuviera que conectar la cámara y la Raspberry, con cualquier batería externa del mercado me valdría. Sin embargo, la montura ecuatorial es otra cosa, ya que necesita 12V-0.75A y por el puerto USB sin ninguna configuración solo pueden entregar 5V-3A.

La montura AZ-GTi no viene equipada con QC o PD. A pesar de que necesita 12V, si lo conectamos a un puerto USB de una batería, solamente le entregará 5V-3A. De hecho, en esa configuración la montura funciona, arranca el HotSpot interno y es posible conectarse a ella para controlarla con la aplicación SynScan Pro, pero el motor no es capaz de mover la carga instalada en ella, o si lo hace, lo hace muy, muy despacio. La solución que detallo a continuación, permite resolver esta situación: se puede conectar la montura a una batería mediante un adaptador QC o PD, y se puede configurar la salida entregada por el adaptador.


Haciendo un poco de ingenieria de compras, se pueden encontrar adaptadores QC 2.0, QC 3.0 o USB PD (en inglés se llaman “QC trigger” o “PD trigger”). Es conveniente que tengan algún tipo de indicador luminoso para saber el valor de la tensión escogida, porque si no es correcta corres el riesgo de quemar el dispositivo que conectes, por ejemplo, asignando 9V a un dispositivo que solo necesita 5V (como una Raspberry Pi, que además, no tiene circuito de protección contra este tipo de subidas).

Adaptadores QC 2.0/3.0 (superior) y PD (inferior)

Dado el bajo precio de estos adaptadores (entorno a los 3-5€), recomiendo comprar dos de golpe, de esa forma en caso de avería o cualquier percance tienes otro de repuesto. Los hay más sofisticados, con una carcasa, pantalla OLED, etc., (por un precio mayor) pero para hacer las primeras pruebas, puede valer uno sin carcasa.

Puedes configurar el adaptador, conectandolo a un puerto PD o QC de la batería, seleccionar el voltaje de 12V y conectarlo en el otro extremo a tu dispositivo.


Presta atención a los voltajes antes de conectar un dispositivo, o corres el riesgo de quemar el dispositivo.

En otro artículo puedes encontrar cómo construir uno de ellos, y además, se explican los principios básicos de cómo funciona el protocolo QC. El protocolo PD es similar.

Bateria y adaptador configurado para 5V.

Mi configuracion es la siguiente:

  • Batería Charmast mini 10Ah
  • Adaptador QC/PD con pantalla. Por el extremo USB C se conecta a un puerto QC/PD de una batería. Por el extremo USB A hembra se conecta el dispositivo que quieres alimentar. Además dispone de una pantalla para ver el voltaje configurado. Tiene dos botones para configurar el voltaje de salida: para subir o bajar el voltaje. El voltaje seleccionado va apareciendo en la pantalla.

Presta atención a los voltajes antes de conectar un dispositivo, o corres el riesgo de quemar el dispositivo. Haz la prueba de configuración sin conectar ningún dispositivo, y cuando tengas clara la selección de voltaje, conecta tu dispositivo.

  • Cable alimentación USB A a DC 2.1 x 5.5 mm acodado 1 M.
Bateria y adaptador configurado para 12V.

Como se ve en la fotografía, el voltaje seleccionado es de 12V.

El consumo de la montura AZGTi ronda los 0.1-1 A, la pantalla del adaptador muestra intermitentemente el consumo también. Con dicha bateria puedo alimentar la montura teóricamente durante más de 8 horas, aunque nunca lo he probado durante tanto tiempo.

Hay que tener en cuenta que estas baterías solamente pueden configurarse por encima de 5V-3A para un solo puerto de salida, por lo que si quieres conectar más de un dispositivo, cada uno debe ser de 5V-3A. Hay baterías en el mercado que permiten utilizar los puertos USB con más carga, pero también son más caras, o grandes.

Yo dispongo de dos baterías Charmast, de 10Ah (es la que se ve en las fotografías) y de 23Ah (largo y ancho como el de un móvil, con un grosor del doble de un móvil). Conecto la montura con un consumo de 12V-1.5A a la de 10Ah, tiene muy poco consumo; y por otro lado conecto la Rpi que consume 5V-3A a la batería de 23Ah. De esa forma aprovecho al máximo cada una, y además puedo gestionar las baterías de forma independiente, porque ambos aparatos tienen consumos diferentes. Como su tamaño es pequeño son relativamente manejables y puedes incorporarlas a la montura con una cinta de velcro, o incluso la de 10Ah, cabe en el compartimento de las pilas de la AZ-GTi.

AZ-GTI con cuña ecuatorial

Ahora ya estoy probando la cuña ecuatorial… parece que me ha costado más de lo que esperaba.

Algunas consideraciones a tener en cuenta con esta montura, puesto que no está hecha para novatos, requiere una cierta curva de aprendizaje.

Hay que actualizar el firmware para que funcione en modo ecuatorial (EQ), puesto que por defecto está programada para modo altacimutal (AZ). Hay disponibles multitud de tutoriales de como hacerlo, yo mismo he preparado uno sobre cómo hacerlo desde un Mac.

Continue reading “AZ-GTI con cuña ecuatorial”

GPS y reloj para Raspberry Pi

Si quieres que tu Raspberry Pi pueda tener GPS, y a la vez sincronizar la fecha y hora, hay una solución muy sencilla: conectala utilizando gpsd con tu movil.

Las Raspberry Pi carecen de un reloj permanente, por lo que si las apagas, pierdes la fecha y la hora que tuviera configurada. Eso no es un problema si la vas a tener conectada a Internet intermitentemente, porque puede recuperar la fecha y la hora de los servidores de NTP disponibles en Internet.

Si utilizas una Raspberry Pi con un Linux basado en raspbian puedes recuperar la fecha y la hora anteriores, porque se incluye el programa fake-hwclock, que guarda la fecha y la hora antes de cada parada, y luego la restaura, pero cuando vuelves a arrancar, se ha producido un retraso con la hora real.

Continue reading “GPS y reloj para Raspberry Pi”

Firmware update of AZGTi mount on Catalina

Skywatcher AZGTi mount is a light mount to use for astronomy. In order to use it with an equatorial wedge, you need to update the firmware. The firmware update program is only available as a Windows application.

There are some forums that describe how to run the firmware update using any flavour of wine.

Building on them I created this guide.

Continue reading “Firmware update of AZGTi mount on Catalina”

Gallery 2

Desde que creamos la web de fotografía del Arxiu Històric del Poblenou, hemos utilizado el software de galería de imágenes Gallery 2.

He probado otros software de galerías de imágenes, pero en cuanto a funcionalidades y prestaciones, Gallery 2 es el mejor. He probado Piwigo (de hecho, mi actual web de fotografías está temporalmente en Piwigo, y es el segundo en mi lista), ZenPhoto, y Coppermine, pero ninguno de ellos es tan versátil o robusto como Gallery.

El equipo de desarrollo de Gallery 2 creó la versión 3 del software, pero nunca llegó a ser tan versátil o robusta como Gallery 2, así que a pesar de haber probado la nueva versión en alguna web, no me decidí a instalarlo.

Desafortunadamente, hace ya años que no hay actividad en la comunidad y han discontinuado, así que he tenido que desempolvar mi PHP para realizar el mantenimiento yo mismo.

He creado recientemente un tema basado en bootstrap 3, y el resultado es bastante bueno. Todavía falta por optimizar algunas cosas, pero la cosa ya promete.

Para verlo, navegad a la web de fotografías del Arxiu Històric del Poblenou.

Repositorio github: