Una Q de Quake en mi editor

Cómo hice Quake Q, una extensión de Visual Studio Code que dibuja la letra Q como el logo de Quake 1 sin cambiar ni un carácter del texto.

https://github.com/cefaloide/quake-vscode


La idea

Quake (1996) es uno de esos juegos que se te quedan grabados, y su logo también: un anillo abierto por arriba atravesado por un clavo. Un día pensé que me gustaría ver ese logo cada vez que escribo una Q en el editor. Solo esa letra; el resto del código tenía que seguir igual.

Parecía tan fácil como cambiar una fuente, pero no lo era.

El problema: VS Code no deja cambiar la fuente de una sola letra

En VS Code, editor.fontFamily se aplica a todo el editor, y la API de extensiones no tiene nada parecido a «esta letra, con esta fuente».

Lo que sí ofrece son las decoraciones (createTextEditorDecorationType). Son las mismas que usan los linters para subrayar errores o GitLens para anotar líneas. Con ellas puedes cambiar el color, el fondo o el borde de un trozo de texto, y añadir contenido antes o después de él (before y after), incluida una imagen.

Con eso había dos caminos:

  1. Inyectar CSS. El valor de la propiedad textDecoration acaba tal cual en el CSS del editor. Por eso, si escribes none; font-family: 'DpQuake', se cuela una regla font-family. Funciona, pero es un truco no documentado y solo sirve si tienes una fuente de Quake instalada. Lo dejé como modo opcional (quakeQ.fontFamily).
  2. Dibujar encima. Hacer la letra transparente y pintar una imagen en su sitio. No depende de ninguna fuente instalada, así que es el modo por defecto.

Cómo funciona por dentro

1. Una decoración que esconde la letra

El primer paso es una decoración que vuelve transparente la letra:

vscode.window.createTextEditorDecorationType({
  color: 'transparent',
  rangeBehavior: vscode.DecorationRangeBehavior.ClosedClosed
});

El carácter sigue ahí. El cursor, la selección, la búsqueda y el copiar y pegar funcionan exactamente igual; la letra solo deja de verse.

La q y la Q comparten el mismo tipo de decoración. La imagen de cada una se elige por rango con renderOptions.

2. Una imagen que no ocupa sitio

Encima de la letra transparente se pinta la imagen con un before:

before: {
  contentIconPath: svgUri,
  width: `${width}px`,
  height: `${lineHeight}px`,
  margin: `0 -${width - overhang}px 0 -${overhang}px`,
  textDecoration: 'none; vertical-align: top; pointer-events: none'
}

Un before normal empujaría el texto hacia la derecha. El truco está en los márgenes negativos:

  • El margen izquierdo retrocede lo que la imagen sobresale por la izquierda (overhang).
  • El margen derecho retrocede el resto.
  • Entre los dos suman exactamente el ancho de la imagen, así que su ancho neto es cero.

Así la imagen se superpone a la letra transparente y las columnas no se mueven ni un píxel.

vertical-align: top alinea la imagen con la parte de arriba de la línea, y pointer-events: none deja que los clics pasen al texto que hay debajo.

3. De una cuadrícula de texto a un SVG

Cada letra está dibujada como una cuadrícula de texto, donde # es un píxel encendido y . uno apagado. Esta es la Q:

.#...#.
#.....#
#..#..#
#..#..#
#..#..#
##.#.##
.#####.
...#...
...#...
...#...

Cada glifo tiene además un ascent: el número de filas que quedan por encima de la línea base. Las demás cuelgan por debajo, como el rabito de la q, y aquí ese rabito es la punta del clavo.

La función buildSvg convierte la cuadrícula en un SVG:

  • Junta los píxeles seguidos de cada fila en un solo rectángulo, para que el SVG sea pequeño.
  • Lo rellena con un degradado vertical de bronce, de #d8b070 a #7a4a20, los dos colores configurables.
  • Usa shape-rendering="crispEdges" para que los bordes salgan nítidos, como en un juego de los 90.

4. Medir sin poder medir

La API no da las medidas reales de la fuente del editor, así que las estimo a partir de la configuración:

  • Ancho de un carácter: unos 0,6 × fontSize + letterSpacing. Casi todas las fuentes monoespaciadas rondan las 0,6 em de ancho.
  • Alto de línea: si editor.lineHeight vale 0, VS Code usa 1,35 × fontSize (1,5 en macOS). Los valores menores que 8 son multiplicadores del tamaño de letra.
  • Línea base: más o menos a mitad de la línea más 0,33 × fontSize.

Como la imagen se superpone a la letra en lugar de sustituirla, un error de un píxel en estas cuentas no descoloca nada. Y si con tu fuente la letra queda un poco alta o baja, se corrige con quakeQ.verticalOffset.

5. El tamaño del píxel

El tamaño de cada «píxel» del dibujo es el menor de tres límites:

const pixel = Math.min(
  width / (COLUMNS + 1),          // que quepa a lo ancho
  baseline / glyph.ascent,        // que no se salga por arriba
  (lineHeight - baseline) / descent // ni por abajo
);

El ancho es el de un carácter multiplicado por quakeQ.scale, que por defecto vale 1,5. Así el logo es algo más grande que una letra y se mete un poco en las vecinas, pero nunca se sale de la línea.

6. Archivos SVG con nombre de hash

contentIconPath necesita la URI de un archivo. Por eso cada SVG se guarda en la carpeta de almacenamiento de la extensión (globalStorageUri), con un nombre sacado de su contenido: los primeros 12 caracteres de su hash SHA-1.

Si el SVG no cambia, el archivo tampoco, y un mapa en memoria evita volver a escribirlo.

7. Solo lo que se ve

La extensión solo busca la Q con una expresión regular en las líneas visibles, más 20 de margen por arriba y por abajo para que al hacer scroll rápido no aparezcan letras sin decorar.

El recálculo se hace con un retraso (debounce) de 50 ms cada vez que:

  • cambian los editores visibles,
  • haces scroll,
  • editas el documento.

Así un archivo de 10.000 líneas cuesta lo mismo que uno de 50.

Cuando cambias cualquier ajuste de la extensión (quakeQ.*) o del editor (editor.*), como el tamaño de letra, todo se reconstruye desde cero.

El diseño de la letra

La letra fue lo que más vueltas dio:

  1. Una q de consola. La primera versión era una q gruesa inspirada en la fuente de consola de Quake.
  2. El logo vectorial. Probé a usar el logo oficial en SVG. Al final preferí volver al pixel art, que encaja mejor con el estilo retro (además, el logo es una marca registrada de id Software).
  3. El logo en píxeles. Redibujé la letra con la forma del logo: un anillo y un clavo con cruceta.
  4. Más grande. A tamaño de editor se veía demasiado fina, así que añadí el ajuste quakeQ.scale para que ocupe algo más que una letra.
  5. Sin cruz. Quité la cruceta del clavo, que a ese tamaño ensuciaba el dibujo.
  6. Anillo redondo. Curvé las puntas hacia dentro y ensanché la base para que pareciera un anillo y no un tridente.

Por último añadí el ajuste quakeQ.letters, para elegir si se sustituye la Q mayúscula (por defecto), la q minúscula o las dos.

Ajustes

AjustePor defectoPara qué sirve
quakeQ.enabledtrueActiva o desactiva el efecto
quakeQ.lettersuppercaseuppercase, lowercase o both
quakeQ.scale1.5Tamaño del logo respecto a una letra
quakeQ.color / quakeQ.shadowColor#d8b070 / #7a4a20Colores del degradado
quakeQ.verticalOffset0Ajuste vertical en píxeles
quakeQ.fontFamily""Usar una fuente de Quake instalada en lugar del dibujo

También hay un comando, Quake Q: Toggle, para activarla y desactivarla.

Herramientas

  • JavaScript sin compilar: sin paso de build, sin TypeScript y sin dependencias. Son dos archivos: extension.js, con las decoraciones y los ajustes, y glyph.js, con los dibujos.
  • Icono y captura generados con código: se crean con Playwright a partir de la misma función buildSvg que usa la extensión, así que siempre coinciden con lo que se ve en el editor.
  • Empaquetado: con vsce.
  • Claude Code: desarrollé la extensión con Claude Code. Yo le explicaba en español lo que quería ver, revisaba las capturas y le pedía ajustes. En el repositorio hay un CLAUDE.md con las convenciones del proyecto: commits en formato Conventional Commits y todo el código y la documentación en inglés.

Limitaciones

  • Medidas estimadas: no son las reales de la fuente, así que con algunas fuentes hay que retocar verticalOffset.
  • Colores fijos: no cambian con el tema de VS Code, aunque el bronce se ve bien tanto en temas claros como oscuros.
  • Solapamiento: con scale por encima de 1, el logo se monta un poco sobre las letras de al lado.
  • Modo fuente: depende de un truco de CSS que una futura versión de VS Code podría romper.
  • Sin tests: de momento no hay tests automáticos.

Pruébala

El código está en github.com/cefaloide/quake-vscode. Para probarla, abre la carpeta en VS Code y pulsa F5. Para instalarla:

npx @vscode/vsce package
code --install-extension quake-q-0.1.0.vsix

Y a partir de ahí, cada Q de tu código será un pequeño homenaje a Quake.

TaulellDigital

Hoy vengo a hablar de un proyecto que hice en la hackovid, una hackatón para combatir los problemas derivados del COVID-19.

Mi proyecto se llama TaulellDigital:

https://www.taulelldigital.cat

Consiste en una aplicación web que conecta a clientes con productores de proximidad. Sólo hace falta buscar en el mapa productores y ponerse en contacto con ellos. Si eres productor de proximidad, TaulellDigital te permite promocionarte y automatizar muchos aspectos relacionados con el marketing tradicional.

Aquí tenéis un vídeo explicativo:

Y el enlace a Github:
https://github.com/cefaloide/taulellDigital

Un saludo!

Arduino Serial MP3 Player YX5300 chip

En esta entrada explicaré como reproducir MP3 con arduino y la placa YX5300 de Catalex:

Este módulo utiliza una tarjeta SD para almacenar las canciones en formato MP3 o WAV. La tarjeta debe estar formateada a FAT32 o FAT16.

Se recomienda guardar los audios en carpetas numeradas, aunque solo se tenga una carpeta, la manera más cómoda (por ejemplo si se quiere reproducir todas las canciones de una carpeta en loop) es seguir la siguiente estructura:

.
├── 01
│   ├── 001-blue-piano.mp3
│   └── 002-gettin-ready.mp3
├── 02
│   ├── 001-ukulele-in-my-heart.mp3
│   ├── 002-inspiring-innovation.mp3
│   └── 003-thanks-for-the-memories.mp3
└── 03
    ├── 001-a-hero-is-born.mp3
    └── 002-lullaby.mp3

Para la conexión del cableado si utilizamos Arduino Uno o Nano será la siguiente:
Serial MP3 Player -> ARDUINO
VCC -> 5V o 3.3V
GND -> GND
TX-> D5
RX -> D6

Si utilizamos Arduino Mega se debe utilizar la siguiente conexión que utiliza el Serial3 (en el código también se tendrán que comentar/descomentar un par de líneas):
Serial MP3 Player -> ARDUINO
VCC -> 5V o 3.3V
GND -> GND
TX-> 15 RX3
RX -> 14 TX3

También deberemos conectar a la salida del Jack 3.5 unos altavoces/auriculares.

Después ya podemos cargar el siguiente código o descargarlo de Github:

/***********************************************************/
// Demo for the Serial MP3 Player Catalex (YX5300 chip)
// Hardware: Serial MP3 Player *1
// Board:  Arduino UNO
// http://www.dx.com/p/uart-control-serial-mp3-music-player-module-for-arduino-avr-arm-pic-blue-silver-342439#.VfHyobPh5z0
//
//
//


// Uncomment SoftwareSerial for Arduino Uno or Nano.

#include <SoftwareSerial.h>

#define ARDUINO_RX 5  //should connect to TX of the Serial MP3 Player module
#define ARDUINO_TX 6  //connect to RX of the module

SoftwareSerial mp3(ARDUINO_RX, ARDUINO_TX);
//#define mp3 Serial3    // Connect the MP3 Serial Player to the Arduino MEGA Serial3 (14 TX3 -> RX, 15 RX3 -> TX)

static int8_t Send_buf[8] = {0}; // Buffer for Send commands.  // BETTER LOCALLY
static uint8_t ansbuf[10] = {0}; // Buffer for the answers.    // BETTER LOCALLY

String mp3Answer;           // Answer from the MP3.

boolean autoResume = true;

/************ Command byte **************************/
#define CMD_NEXT_SONG     0X01  // Play next song.
#define CMD_PREV_SONG     0X02  // Play previous song.
#define CMD_PLAY_W_INDEX  0X03
#define CMD_VOLUME_UP     0X04
#define CMD_VOLUME_DOWN   0X05
#define CMD_SET_VOLUME    0X06

#define CMD_SNG_CYCL_PLAY 0X08  // Single Cycle Play.
#define CMD_SEL_DEV       0X09
#define CMD_SLEEP_MODE    0X0A
#define CMD_WAKE_UP       0X0B
#define CMD_RESET         0X0C
#define CMD_PLAY          0X0D
#define CMD_PAUSE         0X0E
#define CMD_PLAY_FOLDER_FILE 0X0F

#define CMD_STOP_PLAY     0X16
#define CMD_FOLDER_CYCLE  0X17
#define CMD_SHUFFLE_PLAY  0x18 //
#define CMD_SET_SNGL_CYCL 0X19 // Set single cycle.

#define CMD_SET_DAC 0X1A
#define DAC_ON  0X00
#define DAC_OFF 0X01

#define CMD_PLAY_W_VOL    0X22
#define CMD_PLAYING_N     0x4C
#define CMD_QUERY_STATUS      0x42
#define CMD_QUERY_VOLUME      0x43
#define CMD_QUERY_FLDR_TRACKS 0x4e
#define CMD_QUERY_TOT_TRACKS  0x48
#define CMD_QUERY_FLDR_COUNT  0x4f

/************ Opitons **************************/
#define DEV_TF            0X02


/*********************************************************************/

void setup()
{
  Serial.begin(9600);
  mp3.begin(9600);
  delay(500);

  sendCommand(CMD_SEL_DEV, DEV_TF);
  delay(500);
}


void loop()
{
  char c = ' ';

  // If there a char on Serial call sendMP3Command to sendCommand
  if ( Serial.available() )
  {
    c = Serial.read();
    sendMP3Command(c);
  }

  // Check for the answer.
  if (mp3.available())
  {
    Serial.println(decodeMP3Answer());
  }
  delay(100);
}


/********************************************************************************/
/*Function sendMP3Command: seek for a 'c' command and send it to MP3  */
/*Parameter: c. Code for the MP3 Command, 'h' for help.                                                                                                         */
/*Return:  void                                                                */

void sendMP3Command(char c) {
  switch (c) {
    case '?':
    case 'h':
      Serial.println("HELP  ");
      Serial.println(" p = Play");
      Serial.println(" P = Pause");
      Serial.println(" > = Next");
      Serial.println(" < = Previous");
      Serial.println(" + = Volume UP");
      Serial.println(" - = Volume DOWN");
      Serial.println(" c = Query current file");
      Serial.println(" q = Query status");
      Serial.println(" v = Query volume");
      Serial.println(" x = Query folder count");
      Serial.println(" t = Query total file count");
      Serial.println(" 1 = Play folder 1");
      Serial.println(" 2 = Play folder 2");
      Serial.println(" 3 = Play folder 3");
      Serial.println(" 4 = Play folder 4");
      Serial.println(" 5 = Play folder 5");
      Serial.println(" S = Sleep");
      Serial.println(" W = Wake up");
      Serial.println(" r = Reset");
      break;


    case 'p':
      Serial.println("Play ");
      sendCommand(CMD_PLAY, 0);
      break;

    case 'P':
      Serial.println("Pause");
      sendCommand(CMD_PAUSE, 0);
      break;


    case '>':
      Serial.println("Next");
      sendCommand(CMD_NEXT_SONG, 0);
      sendCommand(CMD_PLAYING_N, 0x0000); // ask for the number of file is playing
      break;


    case '<':
      Serial.println("Previous");
      sendCommand(CMD_PREV_SONG, 0);
      sendCommand(CMD_PLAYING_N, 0x0000); // ask for the number of file is playing
      break;

    case '+':
      Serial.println("Volume Up");
      sendCommand(CMD_VOLUME_UP, 0);
      break;

    case '-':
      Serial.println("Volume Down");
      sendCommand(CMD_VOLUME_DOWN, 0);
      break;

    case 'c':
      Serial.println("Query current file");
      sendCommand(CMD_PLAYING_N, 0);
      break;
      
    case 'q':
      Serial.println("Query status");
      sendCommand(CMD_QUERY_STATUS, 0);
      break;

    case 'v':
      Serial.println("Query volume");
      sendCommand(CMD_QUERY_VOLUME, 0);
      break;

    case 'x':
      Serial.println("Query folder count");
      sendCommand(CMD_QUERY_FLDR_COUNT, 0);
      break;

    case 't':
      Serial.println("Query total file count");
      sendCommand(CMD_QUERY_TOT_TRACKS, 0);
      break;

    case '1':
      Serial.println("Play folder 1");
      sendCommand(CMD_FOLDER_CYCLE, 0x0101);
      break;

    case '2':
      Serial.println("Play folder 2");
      sendCommand(CMD_FOLDER_CYCLE, 0x0201);
      break;

    case '3':
      Serial.println("Play folder 3");
      sendCommand(CMD_FOLDER_CYCLE, 0x0301);
      break;

    case '4':
      Serial.println("Play folder 4");
      sendCommand(CMD_FOLDER_CYCLE, 0x0401);
      break;

    case '5':
      Serial.println("Play folder 5");
      sendCommand(CMD_FOLDER_CYCLE, 0x0501);
      break;

    case 'S':
      Serial.println("Sleep");
      sendCommand(CMD_SLEEP_MODE, 0x00);
      break;

    case 'W':
      Serial.println("Wake up");
      sendCommand(CMD_WAKE_UP, 0x00);
      break;

    case 'r':
      Serial.println("Reset");
      sendCommand(CMD_RESET, 0x00);
      break;
  }
}



/********************************************************************************/
/*Function decodeMP3Answer: Decode MP3 answer.                                  */
/*Parameter:-void                                                               */
/*Return: The                                                  */

String decodeMP3Answer() {
  String decodedMP3Answer = "";

  decodedMP3Answer += sanswer();

  switch (ansbuf[3]) {
    case 0x3A:
      decodedMP3Answer += " -> Memory card inserted.";
      break;

    case 0x3D:
      decodedMP3Answer += " -> Completed play num " + String(ansbuf[6], DEC);
      break;

    case 0x40:
      decodedMP3Answer += " -> Error";
      break;

    case 0x41:
      decodedMP3Answer += " -> Data recived correctly. ";
      break;

    case 0x42:
      decodedMP3Answer += " -> Status playing: " + String(ansbuf[6], DEC);
      break;

    case 0x48:
      decodedMP3Answer += " -> File count: " + String(ansbuf[6], DEC);
      break;

    case 0x4C:
      decodedMP3Answer += " -> Playing: " + String(ansbuf[6], DEC);
      break;

    case 0x4E:
      decodedMP3Answer += " -> Folder file count: " + String(ansbuf[6], DEC);
      break;

    case 0x4F:
      decodedMP3Answer += " -> Folder count: " + String(ansbuf[6], DEC);
      break;
  }

  return decodedMP3Answer;
}






/********************************************************************************/
/*Function: Send command to the MP3                                         */
/*Parameter:-int8_t command                                                     */
/*Parameter:-int16_ dat  parameter for the command                              */

void sendCommand(int8_t command, int16_t dat)
{
  delay(20);
  Send_buf[0] = 0x7e;   //
  Send_buf[1] = 0xff;   //
  Send_buf[2] = 0x06;   // Len
  Send_buf[3] = command;//
  Send_buf[4] = 0x01;   // 0x00 NO, 0x01 feedback
  Send_buf[5] = (int8_t)(dat >> 8);  //datah
  Send_buf[6] = (int8_t)(dat);       //datal
  Send_buf[7] = 0xef;   //
  Serial.print("Sending: ");
  for (uint8_t i = 0; i < 8; i++)
  {
    mp3.write(Send_buf[i]) ;
    Serial.print(sbyte2hex(Send_buf[i]));
  }
  Serial.println();
}



/********************************************************************************/
/*Function: sbyte2hex. Returns a byte data in HEX format.                 */
/*Parameter:- uint8_t b. Byte to convert to HEX.                                */
/*Return: String                                                                */


String sbyte2hex(uint8_t b)
{
  String shex;

  shex = "0X";

  if (b < 16) shex += "0";
  shex += String(b, HEX);
  shex += " ";
  return shex;
}




/********************************************************************************/
/*Function: sanswer. Returns a String answer from mp3 UART module.          */
/*Parameter:- uint8_t b. void.                                                  */
/*Return: String. If the answer is well formated answer.                        */

String sanswer(void)
{
  uint8_t i = 0;
  String mp3answer = "";

  // Get only 10 Bytes
  while (mp3.available() && (i < 10))
  {
    uint8_t b = mp3.read();
    ansbuf[i] = b;
    i++;

    mp3answer += sbyte2hex(b);
  }

  // if the answer format is correct.
  if ((ansbuf[0] == 0x7E) && (ansbuf[9] == 0xEF))
  {
    return mp3answer;
  }

  return "???: " + mp3answer;
}

Si estamos trabajando con Arduino Mega se deberá comentar la línea 13

#include <SoftwareSerial.h>

Comentar la línea 18 y descomentar la 19

SoftwareSerial mp3(ARDUINO_RX, ARDUINO_TX);
//#define mp3 Serial3    // Connect the MP3 Serial Player to the Arduino MEGA Serial3 (14 TX3 -> RX, 15 RX3 -> TX)

Ahora podremos manipular el reproductor a través del monitor serie del propio IDE de Arduino. Una vez subido el código al Arduino abrimos el monitor serie:

Si todo está correcto nos mostrará el texto:

Si introducimos la letra «h» nos mostrará el menu de ayuda con las diferentes opciones disponibles:

Si introducimos la letra «p» se ejecutará un audio y si introducimos «1» ejecutará todas las canciones de la carpeta 01.

Para más información sobre el funcionamiento del módulo puedes dirigirte a mi repositorio de Github donde se encuentra más documentación:
https://github.com/cefaloide/ArduinoSerialMP3Player

Referencias:
https://andrologiciels.wordpress.com/arduino/son-et-arduino/mp3/catalex-mp3-serie/

http://www.jarzebski.pl/arduino/komponenty/modul-mp3-z-ukladem-yx5300.html

https://www.carnetdumaker.net/articles/utiliser-un-lecteur-serie-de-fichiers-mp3-avec-une-carte-arduino-genuino/

Archivo ejecutable .bat para abrir chrome en modo kiosk e incognito

Para hacer un archivo ejecutable que abra el navegador Chrome en modo kiosk se debe crear un archivo de texto y guardar en formato .bat con el siguiente contenido:

start chrome.exe --kiosk http://joanruedapauweb.com

Si se quiere abrir una web que se encuentra en el propio ordenador, se puede navegar por las carpetas de la siguiente manera:

::	Ejemplo estructura
::	archivo.bat
::		|->	nombre_carpeta
::				|-> index.html

start chrome.exe --incognito --kiosk %cd%\nombre_carpeta\index.html

En el ejemplo anterior también se abre en modo incógnito.

Servidor Web LAMP Raspberry Pi

Hoy explicaré como hacerte un servidor LAMP (Linux Apache MySQL PHP) con tu Raspberry Pi.

En primer lugar debes asegurarte de tener configurada la IP fija, aquí se explica como.

En segundo lugar debes actualizar el apt-get para preparar las instalaciones, así que en línea de comando escribes las sentencias correspondientes:

sudo apt-get update
sudo apt-get upgrade

Y ya pasamos por fin a las intalaciones 🙂

Instalamos Apache:

sudo apt-get install apache2

Instalamos PHP y varios paquetes:

sudo apt-get install php5
sudo apt-get install libapache2-mod-php5 libapache2-mod-perl2 php5 php5-cli php5-common php5-curl php5-dev php5-gd php5-imap php5-ldap php5-mhash php5-mysql php5-odbc

Podemos crear un archivo phpinfo para ver la información:

sudo nano /var/www/html/info.php

Dentro del archivo escribimos:

<?php
      phpinfo();
?>

Ahora podemos comprobar entrando en el navegador web desde la Raspberry la url: http://localhost/info.php

O si nos conectamos de manera remota (en el ejemplo la ip es 192.168.1.100), introducimos la siguiente url en nuestro navegador web: http://192.168.1.100/info.php

Podremos ver algo parecido a esto:
capturaphpinfo

Instalamos MySQL:

sudo apt-get install mysql-server

Ahora podemos instalar phpMyAdmin para gestionar via web la Base de Datos, para ello primero haremos:

sudo apt-get install libapache2-mod-auth-mysql php5-mysql phpmyadmin

Mientras se instala nos pedirá el tipo de servidor instalado, seleccionamos «apache2».
También nos preguntará la contraseña que tiene la base de datos y nos pedirá una contraseña para el phpMyAdmin.

Ahora queda configurar Apache para trabajar con phpMyAdmin:

sudo nano /etc/apache2/apache2.conf

Y añadimos al final del documento (recuerda que puedes avanzar más rápido con Ctrl+V) la siguiente línea:

Include /etc/phpmyadmin/apache.conf

Guardamos el documento y reiniciamos apache:

sudo /etc/init.d/apache2 restart

Ahora si introducimos en el navegador web de la Rapberry:
http://localhost/phpmyadmin

O si lo hacemos desde remoto (en el ejemplo la ip es 192.168.1.100), introducimos en nuestro navegador web: http://192.168.1.100/phpmyadmin

Nos aparecerá la pantalla de login de phpmyadmin:

capturaphpmyadmin

Ahora ya podemos gestionar por la web la base de datos, bieeen!

Fuentes:
https://geekytheory.com/tutorial-raspberry-pi-crear-servidor-web/

http://www.geothread.net/building-a-lamp-server-on-the-raspberry-pi-computer/

http://raspipress.com/2012/09/tutorial-install-phpmyadmin-on-your-raspberry-pi/

Asignar IP estática (fija) Raspberry Pi

Para saber siempre la IP de tu Raspberry Pi lo que debes hacer es asignarle una IP que tú le especifiques, para ello debemos modificar el archivo «interfaces» de la Raspberry.
Primero podemos hacer una copia del archivo por seguridad:

sudo cp /etc/network/interfaces /etc/network/interfaces_BK

Y ahora modificamos el archivo:

sudo nano /etc/network/interfaces

Dentro del documento yo he tenido que comentar la siguiente línea(puede que en vez de la palabra «manual» aparezca otra):

iface eth0 inet manual

Y Añadir:
-La línea con la palabra «static»
-La dirección que queremos asignarle (en el ejemplo 192.168.1.100)
-La Mascara de red (255.255.255.0)
-La dirección del router (192.168.0.1)

#iface eth0 inet manual
iface eth0 inet static
address 192.168.0.100
netmask 255.255.255.0
gateway 192.168.0.1

La parte anterior sirve para la conexión por cable, si también conectamos la Raspberry por Wifi deberemos hacer lo mismo para la Wifi en el documento, un poquito más abajo:

#iface wlan0 inet manual
iface wlan0 inet static
address 192.168.0.100
netmask 255.255.255.0
gateway 192.168.0.1

Ahora toca reiniciar y ya se conectará con la IP específica.

Fuentes:
http://raspberryparatorpes.net/instalacion/poner-la-direccin-ip-fija-en-raspbian/

https://adrianhontoria.wordpress.com/2013/12/22/ip-fija-en-tu-raspberry-pi/

Pantalla OLED 4Pin Arduino

Hola, voy a explicar como conectar una pantalla OLED de 4Pins como esta:

oled

Si empezamos por el Software, en primer lugar se deben descargar las librerías (library) de Arduino requeridas que proporciona Adafruit:
https://github.com/adafruit/Adafruit_SSD1306
https://github.com/adafruit/Adafruit-GFX-Library

Si no has instalado ninguna librería de Arduino anteriormente consulta este post donde se explican varias maneras de hacerlo.

Después de instalar las bibliotecas, puedes cargar un ejemplo entrando en Archivo-> Ejemplos-> Adafruit SSD1306-> ssd1306_128x64_i2c

ejemplo

Una vez cargamos el ejemplo yo en mi caso he tenido que modificar la siguiente línea:

 display.begin(SSD1306_SWITCHCAPVCC, 0x3D);  // initialize with the I2C addr 0x3D (for the 128x64)

Cambiando 0x3D por 0x3C

 display.begin(SSD1306_SWITCHCAPVCC, 0x3C);  // initialize with the I2C addr 0x3D (for the 128x64)

Ahora vayamos a la conexión del hardware que se debe hacer de la siguiente manera:

esquema OLED arduino

¡Ojo! Yo en mi Oled tengo el VCC y el GND cambiados de sitio, no os fijéis en la posición y fijaros en la palabra que aparece en vuestra OLED.

OLED -> ARDUINO
VCC -> 5V
GND -> GND
SCL -> SCL
SDA -> SDA

¡Ya hemos terminado!

Finalmente si subimos el ejemplo al Arduino podremos ver el resultado.

Fuentes:

 

Importar librerías (library) Arduino

Hay tres maneras de importar una librerías (library) a Arduino:
-Utilizando el Library Manager
-Importando un .zip
-Manualmente

Utilizando el Library Manager
Dentro del menú Programa->Include Library-> Manage Libraries.
Aquí podrás buscar e instalar la librería que quieras.

Importando un .zip
Si tienes un comprimido que te has descargado puedes importar directamente este archivo desde Programa-> Include Library -> Add .zip Library y seleccionar el .zip.

Manualmente
Deberás dejar la carpeta con el contenido (archivo .cpp, archivo .h y otros archivos/caretas como ejemplos) dentro de una determinada carpeta dependiendo de tu SO:

En Windows (carpeta Mis Documentos):
Documentos\Arduino\libraries

En Mac:
Documents/Arduino/libraries

En Linux:
Será la carpeta «libraries» en tu sketchbook.

¡Importante!
Los archivos .cpp y .h se deben encontrar en la carpeta inmediata, no funciona si se encuentran en una subcarpeta, es decir, esta disposición és incorrecta:
Arduino\libraries\Librerias_nuevas\MinuevaLib\archivo.cpp

Debe estar en el primer nivel por debajo de «libraries»:
Arduino\libraries\MinuevaLib\archivo.cpp

Fuentes:
https://www.arduino.cc/en/Guide/Libraries

https://learn.adafruit.com/arduino-tips-tricks-and-techniques/arduino-libraries